]> git.openstreetmap.org Git - rails.git/blob - vendor/assets/iD/iD.js
Merge remote-tracking branch 'upstream/pull/2720'
[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                 }
54823
54824
54825                 function nudgeSelection(delta) {
54826                     return function() {
54827                         // prevent nudging during low zoom selection
54828                         if (!context.map().withinEditableZoom()) { return; }
54829
54830                         var moveOp = operationMove(context, selectedIDs);
54831                         if (moveOp.disabled()) {
54832                             context.ui().flash
54833                                 .duration(4000)
54834                                 .iconName('#iD-operation-' + moveOp.id)
54835                                 .iconClass('operation disabled')
54836                                 .text(moveOp.tooltip)();
54837                         } else {
54838                             context.perform(actionMove(selectedIDs, delta, context.projection), moveOp.annotation());
54839                         }
54840                     };
54841                 }
54842
54843
54844                 function didDoubleUp(loc) {
54845                     if (!context.map().withinEditableZoom()) { return; }
54846
54847                     var target = select(event.target);
54848
54849                     var datum = target.datum();
54850                     var entity = datum && datum.properties && datum.properties.entity;
54851                     if (!entity) { return; }
54852
54853                     if (entity instanceof osmWay && target.classed('target')) {
54854                         var choice = geoChooseEdge(context.graph().childNodes(entity), loc, context.projection);
54855                         var prev = entity.nodes[choice.index - 1];
54856                         var next = entity.nodes[choice.index];
54857
54858                         context.perform(
54859                             actionAddMidpoint({ loc: choice.loc, edge: [prev, next] }, osmNode()),
54860                             _t('operations.add.annotation.vertex')
54861                         );
54862
54863                     } else if (entity.type === 'midpoint') {
54864                         context.perform(
54865                             actionAddMidpoint({ loc: entity.loc, edge: entity.edge }, osmNode()),
54866                             _t('operations.add.annotation.vertex'));
54867                     }
54868                 }
54869
54870
54871                 function selectElements() {
54872                     if (!checkSelectedIDs()) { return; }
54873
54874                     var surface = context.surface();
54875
54876                     surface.selectAll('.selected-member')
54877                         .classed('selected-member', false);
54878
54879                     surface.selectAll('.selected')
54880                         .classed('selected', false);
54881
54882                     surface.selectAll('.related')
54883                         .classed('related', false);
54884
54885                     singularParent();
54886                     if (_relatedParent) {
54887                         surface.selectAll(utilEntitySelector([_relatedParent]))
54888                             .classed('related', true);
54889                     }
54890
54891                     if (context.map().withinEditableZoom()) {
54892                         // Apply selection styling if not in wide selection
54893
54894                         surface
54895                             .selectAll(utilDeepMemberSelector(selectedIDs, context.graph(), true /* skipMultipolgonMembers */))
54896                             .classed('selected-member', true);
54897                         surface
54898                             .selectAll(utilEntityOrDeepMemberSelector(selectedIDs, context.graph()))
54899                             .classed('selected', true);
54900                     }
54901
54902                 }
54903
54904
54905                 function esc() {
54906                     if (context.container().select('.combobox').size()) { return; }
54907                     context.enter(modeBrowse(context));
54908                 }
54909
54910
54911                 function firstVertex() {
54912                     event.preventDefault();
54913                     var entity = singular();
54914                     var parent = singularParent();
54915                     var way;
54916
54917                     if (entity && entity.type === 'way') {
54918                         way = entity;
54919                     } else if (parent) {
54920                         way = context.entity(parent);
54921                     }
54922
54923                     if (way) {
54924                         context.enter(
54925                             modeSelect(context, [way.first()]).follow(true)
54926                         );
54927                     }
54928                 }
54929
54930
54931                 function lastVertex() {
54932                     event.preventDefault();
54933                     var entity = singular();
54934                     var parent = singularParent();
54935                     var way;
54936
54937                     if (entity && entity.type === 'way') {
54938                         way = entity;
54939                     } else if (parent) {
54940                         way = context.entity(parent);
54941                     }
54942
54943                     if (way) {
54944                         context.enter(
54945                             modeSelect(context, [way.last()]).follow(true)
54946                         );
54947                     }
54948                 }
54949
54950
54951                 function previousVertex() {
54952                     event.preventDefault();
54953                     var parent = singularParent();
54954                     if (!parent) { return; }
54955
54956                     var way = context.entity(parent);
54957                     var length = way.nodes.length;
54958                     var curr = way.nodes.indexOf(selectedIDs[0]);
54959                     var index = -1;
54960
54961                     if (curr > 0) {
54962                         index = curr - 1;
54963                     } else if (way.isClosed()) {
54964                         index = length - 2;
54965                     }
54966
54967                     if (index !== -1) {
54968                         context.enter(
54969                             modeSelect(context, [way.nodes[index]]).follow(true)
54970                         );
54971                     }
54972                 }
54973
54974
54975                 function nextVertex() {
54976                     event.preventDefault();
54977                     var parent = singularParent();
54978                     if (!parent) { return; }
54979
54980                     var way = context.entity(parent);
54981                     var length = way.nodes.length;
54982                     var curr = way.nodes.indexOf(selectedIDs[0]);
54983                     var index = -1;
54984
54985                     if (curr < length - 1) {
54986                         index = curr + 1;
54987                     } else if (way.isClosed()) {
54988                         index = 0;
54989                     }
54990
54991                     if (index !== -1) {
54992                         context.enter(
54993                             modeSelect(context, [way.nodes[index]]).follow(true)
54994                         );
54995                     }
54996                 }
54997
54998
54999                 function nextParent() {
55000                     event.preventDefault();
55001                     var parents = commonParents();
55002                     if (!parents || parents.length < 2) { return; }
55003
55004                     var index = parents.indexOf(_relatedParent);
55005                     if (index < 0 || index > parents.length - 2) {
55006                         _relatedParent = parents[0];
55007                     } else {
55008                         _relatedParent = parents[index + 1];
55009                     }
55010
55011                     var surface = context.surface();
55012                     surface.selectAll('.related')
55013                         .classed('related', false);
55014
55015                     if (_relatedParent) {
55016                         surface.selectAll(utilEntitySelector([_relatedParent]))
55017                             .classed('related', true);
55018                     }
55019                 }
55020             };
55021
55022
55023             mode.exit = function() {
55024
55025                 _newFeature = false;
55026
55027                 _operations.forEach(function(operation) {
55028                     if (operation.behavior) {
55029                         context.uninstall(operation.behavior);
55030                     }
55031                 });
55032                 _operations = [];
55033
55034                 _behaviors.forEach(context.uninstall);
55035
55036                 select(document)
55037                     .call(keybinding.unbind);
55038
55039                 context.ui().closeEditMenu();
55040
55041                 context.history()
55042                     .on('change.select', null)
55043                     .on('undone.select', null)
55044                     .on('redone.select', null);
55045
55046                 var surface = context.surface();
55047
55048                 surface
55049                     .selectAll('.selected-member')
55050                     .classed('selected-member', false);
55051
55052                 surface
55053                     .selectAll('.selected')
55054                     .classed('selected', false);
55055
55056                 surface
55057                     .selectAll('.highlighted')
55058                     .classed('highlighted', false);
55059
55060                 surface
55061                     .selectAll('.related')
55062                     .classed('related', false);
55063
55064                 context.map().on('drawn.select', null);
55065                 context.ui().sidebar.hide();
55066                 context.features().forceVisible([]);
55067
55068                 var entity = singular();
55069                 if (_newFeature && entity && entity.type === 'relation' &&
55070                     // no tags
55071                     Object.keys(entity.tags).length === 0 &&
55072                     // no parent relations
55073                     context.graph().parentRelations(entity).length === 0 &&
55074                     // no members or one member with no role
55075                     (entity.members.length === 0 || (entity.members.length === 1 && !entity.members[0].role))
55076                 ) {
55077                     // the user added this relation but didn't edit it at all, so just delete it
55078                     var deleteAction = actionDeleteRelation(entity.id, true /* don't delete untagged members */);
55079                     context.perform(deleteAction, _t('operations.delete.annotation.relation'));
55080                 }
55081             };
55082
55083
55084             return mode;
55085         }
55086
55087         function uiLasso(context) {
55088             var group, polygon;
55089
55090             lasso.coordinates = [];
55091
55092             function lasso(selection) {
55093                 context.container()
55094                     .classed('lasso', true);
55095
55096                 group = selection
55097                     .append('g')
55098                     .attr('class', 'lasso hide');
55099
55100                 polygon = group
55101                     .append('path')
55102                     .attr('class', 'lasso-path');
55103
55104                 group
55105                     .call(uiToggle(true));
55106             }
55107
55108
55109             function draw() {
55110                 if (polygon) {
55111                     polygon.data([lasso.coordinates])
55112                         .attr('d', function(d) { return 'M' + d.join(' L') + ' Z'; });
55113                 }
55114             }
55115
55116
55117             lasso.extent = function () {
55118                 return lasso.coordinates.reduce(function(extent, point) {
55119                     return extent.extend(geoExtent(point));
55120                 }, geoExtent());
55121             };
55122
55123
55124             lasso.p = function(_) {
55125                 if (!arguments.length) { return lasso; }
55126                 lasso.coordinates.push(_);
55127                 draw();
55128                 return lasso;
55129             };
55130
55131
55132             lasso.close = function() {
55133                 if (group) {
55134                     group.call(uiToggle(false, function() {
55135                         select(this).remove();
55136                     }));
55137                 }
55138                 context.container().classed('lasso', false);
55139             };
55140
55141
55142             return lasso;
55143         }
55144
55145         function behaviorLasso(context) {
55146
55147             // use pointer events on supported platforms; fallback to mouse events
55148             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
55149
55150             var behavior = function(selection) {
55151                 var lasso;
55152
55153
55154                 function pointerdown() {
55155                     var button = 0;  // left
55156                     if (event.button === button && event.shiftKey === true) {
55157                         lasso = null;
55158
55159                         select(window)
55160                             .on(_pointerPrefix + 'move.lasso', pointermove)
55161                             .on(_pointerPrefix + 'up.lasso', pointerup);
55162
55163                         event.stopPropagation();
55164                     }
55165                 }
55166
55167
55168                 function pointermove() {
55169                     if (!lasso) {
55170                         lasso = uiLasso(context);
55171                         context.surface().call(lasso);
55172                     }
55173
55174                     lasso.p(context.map().mouse());
55175                 }
55176
55177
55178                 function normalize(a, b) {
55179                     return [
55180                         [Math.min(a[0], b[0]), Math.min(a[1], b[1])],
55181                         [Math.max(a[0], b[0]), Math.max(a[1], b[1])]
55182                     ];
55183                 }
55184
55185
55186                 function lassoed() {
55187                     if (!lasso) { return []; }
55188
55189                     var graph = context.graph();
55190                     var limitToNodes;
55191
55192                     if (context.map().editableDataEnabled(true /* skipZoomCheck */) && context.map().isInWideSelection()) {
55193                         // only select from the visible nodes
55194                         limitToNodes = new Set(utilGetAllNodes(context.selectedIDs(), graph));
55195                     } else if (!context.map().editableDataEnabled()) {
55196                         return [];
55197                     }
55198
55199                     var bounds = lasso.extent().map(context.projection.invert);
55200                     var extent = geoExtent(normalize(bounds[0], bounds[1]));
55201
55202                     var intersects = context.history().intersects(extent).filter(function(entity) {
55203                         return entity.type === 'node' &&
55204                             (!limitToNodes || limitToNodes.has(entity)) &&
55205                             geoPointInPolygon(context.projection(entity.loc), lasso.coordinates) &&
55206                             !context.features().isHidden(entity, graph, entity.geometry(graph));
55207                     });
55208
55209                     // sort the lassoed nodes as best we can
55210                     intersects.sort(function(node1, node2) {
55211                         var parents1 = graph.parentWays(node1);
55212                         var parents2 = graph.parentWays(node2);
55213                         if (parents1.length && parents2.length) {
55214                             // both nodes are vertices
55215
55216                             var sharedParents = utilArrayIntersection(parents1, parents2);
55217                             if (sharedParents.length) {
55218                                 var sharedParentNodes = sharedParents[0].nodes;
55219                                 // vertices are members of the same way; sort them in their listed order
55220                                 return sharedParentNodes.indexOf(node1.id) -
55221                                     sharedParentNodes.indexOf(node2.id);
55222                             } else {
55223                                 // vertices do not share a way; group them by their respective parent ways
55224                                 return parseFloat(parents1[0].id.slice(1)) -
55225                                     parseFloat(parents2[0].id.slice(1));
55226                             }
55227
55228                         } else if (parents1.length || parents2.length) {
55229                             // only one node is a vertex; sort standalone points before vertices
55230                             return parents1.length - parents2.length;
55231                         }
55232                         // both nodes are standalone points; sort left to right
55233                         return node1.loc[0] - node2.loc[0];
55234                     });
55235
55236                     return intersects.map(function(entity) { return entity.id; });
55237                 }
55238
55239
55240                 function pointerup() {
55241                     select(window)
55242                         .on(_pointerPrefix + 'move.lasso', null)
55243                         .on(_pointerPrefix + 'up.lasso', null);
55244
55245                     if (!lasso) { return; }
55246
55247                     var ids = lassoed();
55248                     lasso.close();
55249
55250                     if (ids.length) {
55251                         context.enter(modeSelect(context, ids));
55252                     }
55253                 }
55254
55255                 selection
55256                     .on(_pointerPrefix + 'down.lasso', pointerdown);
55257             };
55258
55259
55260             behavior.off = function(selection) {
55261                 selection.on(_pointerPrefix + 'down.lasso', null);
55262             };
55263
55264
55265             return behavior;
55266         }
55267
55268         function modeBrowse(context) {
55269             var mode = {
55270                 button: 'browse',
55271                 id: 'browse',
55272                 title: _t('modes.browse.title'),
55273                 description: _t('modes.browse.description')
55274             };
55275             var sidebar;
55276
55277             var _selectBehavior;
55278             var _behaviors = [];
55279
55280
55281             mode.selectBehavior = function(val) {
55282                 if (!arguments.length) { return _selectBehavior; }
55283                 _selectBehavior = val;
55284                 return mode;
55285             };
55286
55287
55288             mode.enter = function() {
55289                 if (!_behaviors.length) {
55290                     if (!_selectBehavior) { _selectBehavior = behaviorSelect(context); }
55291                     _behaviors = [
55292                         behaviorPaste(context),
55293                         behaviorHover(context).on('hover', context.ui().sidebar.hover),
55294                         _selectBehavior,
55295                         behaviorLasso(context),
55296                         modeDragNode(context).behavior,
55297                         modeDragNote(context).behavior
55298                     ];
55299                 }
55300                 _behaviors.forEach(context.install);
55301
55302                 // Get focus on the body.
55303                 if (document.activeElement && document.activeElement.blur) {
55304                     document.activeElement.blur();
55305                 }
55306
55307                 if (sidebar) {
55308                     context.ui().sidebar.show(sidebar);
55309                 } else {
55310                     context.ui().sidebar.select(null);
55311                 }
55312             };
55313
55314
55315             mode.exit = function() {
55316                 context.ui().sidebar.hover.cancel();
55317                 _behaviors.forEach(context.uninstall);
55318
55319                 if (sidebar) {
55320                     context.ui().sidebar.hide();
55321                 }
55322             };
55323
55324
55325             mode.sidebar = function(_) {
55326                 if (!arguments.length) { return sidebar; }
55327                 sidebar = _;
55328                 return mode;
55329             };
55330
55331
55332             mode.operations = function() {
55333                 return [operationPaste(context)];
55334             };
55335
55336
55337             return mode;
55338         }
55339
55340         function behaviorAddWay(context) {
55341             var dispatch$1 = dispatch('start', 'startFromWay', 'startFromNode');
55342             var draw = behaviorDraw(context);
55343
55344             function behavior(surface) {
55345                 draw.on('click', function() { dispatch$1.apply('start', this, arguments); })
55346                     .on('clickWay', function() { dispatch$1.apply('startFromWay', this, arguments); })
55347                     .on('clickNode', function() { dispatch$1.apply('startFromNode', this, arguments); })
55348                     .on('cancel', behavior.cancel)
55349                     .on('finish', behavior.cancel);
55350
55351                 context.map()
55352                     .dblclickZoomEnable(false);
55353
55354                 surface.call(draw);
55355             }
55356
55357
55358             behavior.off = function(surface) {
55359                 surface.call(draw.off);
55360             };
55361
55362
55363             behavior.cancel = function() {
55364                 window.setTimeout(function() {
55365                     context.map().dblclickZoomEnable(true);
55366                 }, 1000);
55367
55368                 context.enter(modeBrowse(context));
55369             };
55370
55371
55372             return utilRebind(behavior, dispatch$1, 'on');
55373         }
55374
55375         function behaviorHash(context) {
55376
55377             // cached window.location.hash
55378             var _cachedHash = null;
55379             // allowable latitude range
55380             var _latitudeLimit = 90 - 1e-8;
55381
55382             function computedHashParameters() {
55383                 var map = context.map();
55384                 var center = map.center();
55385                 var zoom = map.zoom();
55386                 var precision = Math.max(0, Math.ceil(Math.log(zoom) / Math.LN2));
55387                 var oldParams = utilObjectOmit(utilStringQs(window.location.hash),
55388                     ['comment', 'source', 'hashtags', 'walkthrough']
55389                 );
55390                 var newParams = {};
55391
55392                 delete oldParams.id;
55393                 var selected = context.selectedIDs().filter(function(id) {
55394                     return context.hasEntity(id);
55395                 });
55396                 if (selected.length) {
55397                     newParams.id = selected.join(',');
55398                 }
55399
55400                 newParams.map = zoom.toFixed(2) +
55401                     '/' + center[1].toFixed(precision) +
55402                     '/' + center[0].toFixed(precision);
55403
55404                 return Object.assign(oldParams, newParams);
55405             }
55406
55407             function computedHash() {
55408                 return '#' + utilQsString(computedHashParameters(), true);
55409             }
55410
55411             function computedTitle(includeChangeCount) {
55412
55413                 var baseTitle = context.documentTitleBase() || 'iD';
55414                 var contextual;
55415                 var changeCount;
55416                 var titleID;
55417
55418                 var selected = context.selectedIDs().filter(function(id) {
55419                     return context.hasEntity(id);
55420                 });
55421                 if (selected.length) {
55422                     var firstLabel = utilDisplayLabel(context.entity(selected[0]), context.graph());
55423                     if (selected.length > 1 ) {
55424                         contextual = _t('title.labeled_and_more', {
55425                             labeled: firstLabel,
55426                             count: (selected.length - 1).toString()
55427                         });
55428                     } else {
55429                         contextual = firstLabel;
55430                     }
55431                     titleID = 'context';
55432                 }
55433
55434                 if (includeChangeCount) {
55435                     changeCount = context.history().difference().summary().length;
55436                     if (changeCount > 0) {
55437                         titleID = contextual ? 'changes_context' : 'changes';
55438                     }
55439                 }
55440
55441                 if (titleID) {
55442                     return _t('title.format.' + titleID, {
55443                         changes: changeCount,
55444                         base: baseTitle,
55445                         context: contextual
55446                     });
55447                 }
55448
55449                 return baseTitle;
55450             }
55451
55452             function updateTitle(includeChangeCount) {
55453                 if (!context.setsDocumentTitle()) { return; }
55454
55455                 var newTitle = computedTitle(includeChangeCount);
55456                 if (document.title !== newTitle) {
55457                     document.title = newTitle;
55458                 }
55459             }
55460
55461             function updateHashIfNeeded() {
55462                 if (context.inIntro()) { return; }
55463
55464                 var latestHash = computedHash();
55465                 if (_cachedHash !== latestHash) {
55466                     _cachedHash = latestHash;
55467
55468                     // Update the URL hash without affecting the browser navigation stack,
55469                     // though unavoidably creating a browser history entry
55470                     window.history.replaceState(null, computedTitle(false /* includeChangeCount */), latestHash);
55471
55472                     // set the title we want displayed for the browser tab/window
55473                     updateTitle(true /* includeChangeCount */);
55474                 }
55475             }
55476
55477             var _throttledUpdate = throttle(updateHashIfNeeded, 500);
55478             var _throttledUpdateTitle = throttle(function() {
55479                 updateTitle(true /* includeChangeCount */);
55480             }, 500);
55481
55482             function hashchange() {
55483
55484                 // ignore spurious hashchange events
55485                 if (window.location.hash === _cachedHash) { return; }
55486
55487                 _cachedHash = window.location.hash;
55488
55489                 var q = utilStringQs(_cachedHash);
55490                 var mapArgs = (q.map || '').split('/').map(Number);
55491
55492                 if (mapArgs.length < 3 || mapArgs.some(isNaN)) {
55493                     // replace bogus hash
55494                     updateHashIfNeeded();
55495
55496                 } else {
55497                     // don't update if the new hash already reflects the state of iD
55498                     if (_cachedHash === computedHash()) { return; }
55499
55500                     var mode = context.mode();
55501
55502                     context.map().centerZoom([mapArgs[2], Math.min(_latitudeLimit, Math.max(-_latitudeLimit, mapArgs[1]))], mapArgs[0]);
55503
55504                     if (q.id) {
55505                         var ids = q.id.split(',').filter(function(id) {
55506                             return context.hasEntity(id);
55507                         });
55508                         var skip = mode && mode.id === 'select' && utilArrayIdentical(mode.selectedIDs(), ids);
55509                         if (ids.length && !skip) {
55510                             context.enter(modeSelect(context, ids));
55511                             return;
55512                         }
55513                     }
55514
55515                     var center = context.map().center();
55516                     var dist = geoSphericalDistance(center, [mapArgs[2], mapArgs[1]]);
55517                     var maxdist = 500;
55518
55519                     // Don't allow the hash location to change too much while drawing
55520                     // This can happen if the user accidently hit the back button.  #3996
55521                     if (mode && mode.id.match(/^draw/) !== null && dist > maxdist) {
55522                         context.enter(modeBrowse(context));
55523                         return;
55524                     }
55525                 }
55526             }
55527
55528             function behavior() {
55529                 context.map()
55530                     .on('move.behaviorHash', _throttledUpdate);
55531
55532                 context.history()
55533                     .on('change.behaviorHash', _throttledUpdateTitle);
55534
55535                 context
55536                     .on('enter.behaviorHash', _throttledUpdate);
55537
55538                 select(window)
55539                     .on('hashchange.behaviorHash', hashchange);
55540
55541                 if (window.location.hash) {
55542                     var q = utilStringQs(window.location.hash);
55543
55544                     if (q.id) {
55545                         //if (!context.history().hasRestorableChanges()) {
55546                             // targeting specific features: download, select, and zoom to them
55547                             context.zoomToEntity(q.id.split(',')[0], !q.map);
55548                         //}
55549                     }
55550
55551                     if (q.walkthrough === 'true') {
55552                         behavior.startWalkthrough = true;
55553                     }
55554
55555                     if (q.map) {
55556                         behavior.hadHash = true;
55557                     }
55558
55559                     hashchange();
55560
55561                     updateTitle(false);
55562                 }
55563             }
55564
55565             behavior.off = function() {
55566                 _throttledUpdate.cancel();
55567                 _throttledUpdateTitle.cancel();
55568
55569                 context.map()
55570                     .on('move.behaviorHash', null);
55571
55572                 context
55573                     .on('enter.behaviorHash', null);
55574
55575                 select(window)
55576                     .on('hashchange.behaviorHash', null);
55577
55578                 window.location.hash = '';
55579             };
55580
55581             return behavior;
55582         }
55583
55584         /*
55585             iD.coreDifference represents the difference between two graphs.
55586             It knows how to calculate the set of entities that were
55587             created, modified, or deleted, and also contains the logic
55588             for recursively extending a difference to the complete set
55589             of entities that will require a redraw, taking into account
55590             child and parent relationships.
55591          */
55592         function coreDifference(base, head) {
55593             var _changes = {};
55594             var _didChange = {};  // 'addition', 'deletion', 'geometry', 'properties'
55595             var _diff = {};
55596
55597             function checkEntityID(id) {
55598                 var h = head.entities[id];
55599                 var b = base.entities[id];
55600
55601                 if (h === b) { return; }
55602                 if (_changes[id]) { return; }
55603
55604                 if (!h && b) {
55605                     _changes[id] = { base: b, head: h };
55606                     _didChange.deletion = true;
55607                     return;
55608                 }
55609                 if (h && !b) {
55610                     _changes[id] = { base: b, head: h };
55611                     _didChange.addition = true;
55612                     return;
55613                 }
55614
55615                 if (h && b) {
55616                     if (h.members && b.members && !fastDeepEqual(h.members, b.members)) {
55617                         _changes[id] = { base: b, head: h };
55618                         _didChange.geometry = true;
55619                         _didChange.properties = true;
55620                         return;
55621                     }
55622                     if (h.loc && b.loc && !geoVecEqual(h.loc, b.loc)) {
55623                         _changes[id] = { base: b, head: h };
55624                         _didChange.geometry = true;
55625                     }
55626                     if (h.nodes && b.nodes && !fastDeepEqual(h.nodes, b.nodes)) {
55627                         _changes[id] = { base: b, head: h };
55628                         _didChange.geometry = true;
55629                     }
55630                     if (h.tags && b.tags && !fastDeepEqual(h.tags, b.tags)) {
55631                         _changes[id] = { base: b, head: h };
55632                         _didChange.properties = true;
55633                     }
55634                 }
55635             }
55636
55637             function load() {
55638                 // HOT CODE: there can be many thousands of downloaded entities, so looping
55639                 // through them all can become a performance bottleneck. Optimize by
55640                 // resolving duplicates and using a basic `for` loop
55641                 var ids = utilArrayUniq(Object.keys(head.entities).concat(Object.keys(base.entities)));
55642                 for (var i = 0; i < ids.length; i++) {
55643                     checkEntityID(ids[i]);
55644                 }
55645             }
55646             load();
55647
55648
55649             _diff.length = function length() {
55650                 return Object.keys(_changes).length;
55651             };
55652
55653
55654             _diff.changes = function changes() {
55655                 return _changes;
55656             };
55657
55658             _diff.didChange = _didChange;
55659
55660
55661             // pass true to include affected relation members
55662             _diff.extantIDs = function extantIDs(includeRelMembers) {
55663                 var result = new Set();
55664                 Object.keys(_changes).forEach(function(id) {
55665                     if (_changes[id].head) {
55666                         result.add(id);
55667                     }
55668
55669                     var h = _changes[id].head;
55670                     var b = _changes[id].base;
55671                     var entity = h || b;
55672
55673                     if (includeRelMembers && entity.type === 'relation') {
55674                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55675                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55676                         utilArrayUnion(mh, mb).forEach(function(memberID) {
55677                             if (head.hasEntity(memberID)) {
55678                                 result.add(memberID);
55679                             }
55680                         });
55681                     }
55682                 });
55683
55684                 return Array.from(result);
55685             };
55686
55687
55688             _diff.modified = function modified() {
55689                 var result = [];
55690                 Object.values(_changes).forEach(function(change) {
55691                     if (change.base && change.head) {
55692                         result.push(change.head);
55693                     }
55694                 });
55695                 return result;
55696             };
55697
55698
55699             _diff.created = function created() {
55700                 var result = [];
55701                 Object.values(_changes).forEach(function(change) {
55702                     if (!change.base && change.head) {
55703                         result.push(change.head);
55704                     }
55705                 });
55706                 return result;
55707             };
55708
55709
55710             _diff.deleted = function deleted() {
55711                 var result = [];
55712                 Object.values(_changes).forEach(function(change) {
55713                     if (change.base && !change.head) {
55714                         result.push(change.base);
55715                     }
55716                 });
55717                 return result;
55718             };
55719
55720
55721             _diff.summary = function summary() {
55722                 var relevant = {};
55723
55724                 var keys = Object.keys(_changes);
55725                 for (var i = 0; i < keys.length; i++) {
55726                     var change = _changes[keys[i]];
55727
55728                     if (change.head && change.head.geometry(head) !== 'vertex') {
55729                         addEntity(change.head, head, change.base ? 'modified' : 'created');
55730
55731                     } else if (change.base && change.base.geometry(base) !== 'vertex') {
55732                         addEntity(change.base, base, 'deleted');
55733
55734                     } else if (change.base && change.head) { // modified vertex
55735                         var moved    = !fastDeepEqual(change.base.loc,  change.head.loc);
55736                         var retagged = !fastDeepEqual(change.base.tags, change.head.tags);
55737
55738                         if (moved) {
55739                             addParents(change.head);
55740                         }
55741
55742                         if (retagged || (moved && change.head.hasInterestingTags())) {
55743                             addEntity(change.head, head, 'modified');
55744                         }
55745
55746                     } else if (change.head && change.head.hasInterestingTags()) { // created vertex
55747                         addEntity(change.head, head, 'created');
55748
55749                     } else if (change.base && change.base.hasInterestingTags()) { // deleted vertex
55750                         addEntity(change.base, base, 'deleted');
55751                     }
55752                 }
55753
55754                 return Object.values(relevant);
55755
55756
55757                 function addEntity(entity, graph, changeType) {
55758                     relevant[entity.id] = {
55759                         entity: entity,
55760                         graph: graph,
55761                         changeType: changeType
55762                     };
55763                 }
55764
55765                 function addParents(entity) {
55766                     var parents = head.parentWays(entity);
55767                     for (var j = parents.length - 1; j >= 0; j--) {
55768                         var parent = parents[j];
55769                         if (!(parent.id in relevant)) {
55770                             addEntity(parent, head, 'modified');
55771                         }
55772                     }
55773                 }
55774             };
55775
55776
55777             // returns complete set of entities that require a redraw
55778             //  (optionally within given `extent`)
55779             _diff.complete = function complete(extent) {
55780                 var result = {};
55781                 var id, change;
55782
55783                 for (id in _changes) {
55784                     change = _changes[id];
55785
55786                     var h = change.head;
55787                     var b = change.base;
55788                     var entity = h || b;
55789                     var i;
55790
55791                     if (extent &&
55792                         (!h || !h.intersects(extent, head)) &&
55793                         (!b || !b.intersects(extent, base)))
55794                         { continue; }
55795
55796                     result[id] = h;
55797
55798                     if (entity.type === 'way') {
55799                         var nh = h ? h.nodes : [];
55800                         var nb = b ? b.nodes : [];
55801                         var diff;
55802
55803                         diff = utilArrayDifference(nh, nb);
55804                         for (i = 0; i < diff.length; i++) {
55805                             result[diff[i]] = head.hasEntity(diff[i]);
55806                         }
55807
55808                         diff = utilArrayDifference(nb, nh);
55809                         for (i = 0; i < diff.length; i++) {
55810                             result[diff[i]] = head.hasEntity(diff[i]);
55811                         }
55812                     }
55813
55814                     if (entity.type === 'relation' && entity.isMultipolygon()) {
55815                         var mh = h ? h.members.map(function(m) { return m.id; }) : [];
55816                         var mb = b ? b.members.map(function(m) { return m.id; }) : [];
55817                         var ids = utilArrayUnion(mh, mb);
55818                         for (i = 0; i < ids.length; i++) {
55819                             var member = head.hasEntity(ids[i]);
55820                             if (!member) { continue; }   // not downloaded
55821                             if (extent && !member.intersects(extent, head)) { continue; }   // not visible
55822                             result[ids[i]] = member;
55823                         }
55824                     }
55825
55826                     addParents(head.parentWays(entity), result);
55827                     addParents(head.parentRelations(entity), result);
55828                 }
55829
55830                 return result;
55831
55832
55833                 function addParents(parents, result) {
55834                     for (var i = 0; i < parents.length; i++) {
55835                         var parent = parents[i];
55836                         if (parent.id in result) { continue; }
55837
55838                         result[parent.id] = parent;
55839                         addParents(head.parentRelations(parent), result);
55840                     }
55841                 }
55842             };
55843
55844
55845             return _diff;
55846         }
55847
55848         function coreTree(head) {
55849             // tree for entities
55850             var _rtree = new RBush();
55851             var _bboxes = {};
55852
55853             // maintain a separate tree for granular way segments
55854             var _segmentsRTree = new RBush();
55855             var _segmentsBBoxes = {};
55856             var _segmentsByWayId = {};
55857
55858             var tree = {};
55859
55860
55861             function entityBBox(entity) {
55862                 var bbox = entity.extent(head).bbox();
55863                 bbox.id = entity.id;
55864                 _bboxes[entity.id] = bbox;
55865                 return bbox;
55866             }
55867
55868
55869             function segmentBBox(segment) {
55870                 var extent = segment.extent(head);
55871                 // extent can be null if the node entites aren't in the graph for some reason
55872                 if (!extent) { return null; }
55873
55874                 var bbox = extent.bbox();
55875                 bbox.segment = segment;
55876                 _segmentsBBoxes[segment.id] = bbox;
55877                 return bbox;
55878             }
55879
55880
55881             function removeEntity(entity) {
55882                 _rtree.remove(_bboxes[entity.id]);
55883                 delete _bboxes[entity.id];
55884
55885                 if (_segmentsByWayId[entity.id]) {
55886                     _segmentsByWayId[entity.id].forEach(function(segment) {
55887                         _segmentsRTree.remove(_segmentsBBoxes[segment.id]);
55888                         delete _segmentsBBoxes[segment.id];
55889                     });
55890                     delete _segmentsByWayId[entity.id];
55891                 }
55892             }
55893
55894
55895             function loadEntities(entities) {
55896                 _rtree.load(entities.map(entityBBox));
55897
55898                 var segments = [];
55899                 entities.forEach(function(entity) {
55900                     if (entity.segments) {
55901                         var entitySegments = entity.segments(head);
55902                         // cache these to make them easy to remove later
55903                         _segmentsByWayId[entity.id] = entitySegments;
55904                         segments = segments.concat(entitySegments);
55905                     }
55906                 });
55907                 if (segments.length) { _segmentsRTree.load(segments.map(segmentBBox).filter(Boolean)); }
55908             }
55909
55910
55911             function updateParents(entity, insertions, memo) {
55912                 head.parentWays(entity).forEach(function(way) {
55913                     if (_bboxes[way.id]) {
55914                         removeEntity(way);
55915                         insertions[way.id] = way;
55916                     }
55917                     updateParents(way, insertions, memo);
55918                 });
55919
55920                 head.parentRelations(entity).forEach(function(relation) {
55921                     if (memo[entity.id]) { return; }
55922                     memo[entity.id] = true;
55923                     if (_bboxes[relation.id]) {
55924                         removeEntity(relation);
55925                         insertions[relation.id] = relation;
55926                     }
55927                     updateParents(relation, insertions, memo);
55928                 });
55929             }
55930
55931
55932             tree.rebase = function(entities, force) {
55933                 var insertions = {};
55934
55935                 for (var i = 0; i < entities.length; i++) {
55936                     var entity = entities[i];
55937                     if (!entity.visible) { continue; }
55938
55939                     if (head.entities.hasOwnProperty(entity.id) || _bboxes[entity.id]) {
55940                         if (!force) {
55941                             continue;
55942                         } else if (_bboxes[entity.id]) {
55943                             removeEntity(entity);
55944                         }
55945                     }
55946
55947                     insertions[entity.id] = entity;
55948                     updateParents(entity, insertions, {});
55949                 }
55950
55951                 loadEntities(Object.values(insertions));
55952
55953                 return tree;
55954             };
55955
55956
55957             function updateToGraph(graph) {
55958                 if (graph === head) { return; }
55959
55960                 var diff = coreDifference(head, graph);
55961
55962                 head = graph;
55963
55964                 var changed = diff.didChange;
55965                 if (!changed.addition && !changed.deletion && !changed.geometry) { return; }
55966
55967                 var insertions = {};
55968
55969                 if (changed.deletion) {
55970                     diff.deleted().forEach(function(entity) {
55971                         removeEntity(entity);
55972                     });
55973                 }
55974
55975                 if (changed.geometry) {
55976                     diff.modified().forEach(function(entity) {
55977                         removeEntity(entity);
55978                         insertions[entity.id] = entity;
55979                         updateParents(entity, insertions, {});
55980                     });
55981                 }
55982
55983                 if (changed.addition) {
55984                     diff.created().forEach(function(entity) {
55985                         insertions[entity.id] = entity;
55986                     });
55987                 }
55988
55989                 loadEntities(Object.values(insertions));
55990             }
55991
55992             // returns an array of entities with bounding boxes overlapping `extent` for the given `graph`
55993             tree.intersects = function(extent, graph) {
55994                 updateToGraph(graph);
55995                 return _rtree.search(extent.bbox())
55996                     .map(function(bbox) { return graph.entity(bbox.id); });
55997             };
55998
55999             // returns an array of segment objects with bounding boxes overlapping `extent` for the given `graph`
56000             tree.waySegments = function(extent, graph) {
56001                 updateToGraph(graph);
56002                 return _segmentsRTree.search(extent.bbox())
56003                     .map(function(bbox) { return bbox.segment; });
56004             };
56005
56006
56007             return tree;
56008         }
56009
56010         function uiModal(selection, blocking) {
56011           var this$1 = this;
56012
56013           var keybinding = utilKeybinding('modal');
56014           var previous = selection.select('div.modal');
56015           var animate = previous.empty();
56016
56017           previous.transition()
56018             .duration(200)
56019             .style('opacity', 0)
56020             .remove();
56021
56022           var shaded = selection
56023             .append('div')
56024             .attr('class', 'shaded')
56025             .style('opacity', 0);
56026
56027           shaded.close = function () {
56028             shaded
56029               .transition()
56030               .duration(200)
56031               .style('opacity',0)
56032               .remove();
56033
56034             modal
56035               .transition()
56036               .duration(200)
56037               .style('top','0px');
56038
56039             select(document)
56040               .call(keybinding.unbind);
56041           };
56042
56043
56044           var modal = shaded
56045             .append('div')
56046             .attr('class', 'modal fillL');
56047
56048           if (!blocking) {
56049             shaded.on('click.remove-modal', function () {
56050               if (event.target === this$1) {
56051                 shaded.close();
56052               }
56053             });
56054
56055             modal
56056               .append('button')
56057               .attr('class', 'close')
56058               .on('click', shaded.close)
56059               .call(svgIcon('#iD-icon-close'));
56060
56061             keybinding
56062               .on('⌫', shaded.close)
56063               .on('⎋', shaded.close);
56064
56065             select(document)
56066               .call(keybinding);
56067           }
56068
56069           modal
56070             .append('div')
56071             .attr('class', 'content');
56072
56073           if (animate) {
56074             shaded.transition().style('opacity', 1);
56075           } else {
56076             shaded.style('opacity', 1);
56077           }
56078
56079           return shaded;
56080         }
56081
56082         function uiLoading(context) {
56083           var _modalSelection = select(null);
56084           var _message = '';
56085           var _blocking = false;
56086
56087
56088           var loading = function (selection) {
56089             _modalSelection = uiModal(selection, _blocking);
56090
56091             var loadertext = _modalSelection.select('.content')
56092               .classed('loading-modal', true)
56093               .append('div')
56094               .attr('class', 'modal-section fillL');
56095
56096             loadertext
56097               .append('img')
56098               .attr('class', 'loader')
56099               .attr('src', context.imagePath('loader-white.gif'));
56100
56101             loadertext
56102               .append('h3')
56103               .text(_message);
56104
56105             _modalSelection.select('button.close')
56106               .attr('class', 'hide');
56107
56108             return loading;
56109           };
56110
56111
56112           loading.message = function(val) {
56113             if (!arguments.length) { return _message; }
56114             _message = val;
56115             return loading;
56116           };
56117
56118
56119           loading.blocking = function(val) {
56120             if (!arguments.length) { return _blocking; }
56121             _blocking = val;
56122             return loading;
56123           };
56124
56125
56126           loading.close = function () {
56127             _modalSelection.remove();
56128           };
56129
56130
56131           loading.isShown = function () {
56132             return _modalSelection && !_modalSelection.empty() && _modalSelection.node().parentNode;
56133           };
56134
56135
56136           return loading;
56137         }
56138
56139         function coreHistory(context) {
56140             var dispatch$1 = dispatch('change', 'merge', 'restore', 'undone', 'redone');
56141             var lock = utilSessionMutex('lock');
56142
56143             // restorable if iD not open in another window/tab and a saved history exists in localStorage
56144             var _hasUnresolvedRestorableChanges = lock.lock() && !!corePreferences(getKey('saved_history'));
56145
56146             var duration = 150;
56147             var _imageryUsed = [];
56148             var _photoOverlaysUsed = [];
56149             var _checkpoints = {};
56150             var _pausedGraph;
56151             var _stack;
56152             var _index;
56153             var _tree;
56154
56155
56156             // internal _act, accepts list of actions and eased time
56157             function _act(actions, t) {
56158                 actions = Array.prototype.slice.call(actions);
56159
56160                 var annotation;
56161                 if (typeof actions[actions.length - 1] !== 'function') {
56162                     annotation = actions.pop();
56163                 }
56164
56165                 var graph = _stack[_index].graph;
56166                 for (var i = 0; i < actions.length; i++) {
56167                     graph = actions[i](graph, t);
56168                 }
56169
56170                 return {
56171                     graph: graph,
56172                     annotation: annotation,
56173                     imageryUsed: _imageryUsed,
56174                     photoOverlaysUsed: _photoOverlaysUsed,
56175                     transform: context.projection.transform(),
56176                     selectedIDs: context.selectedIDs()
56177                 };
56178             }
56179
56180
56181             // internal _perform with eased time
56182             function _perform(args, t) {
56183                 var previous = _stack[_index].graph;
56184                 _stack = _stack.slice(0, _index + 1);
56185                 var actionResult = _act(args, t);
56186                 _stack.push(actionResult);
56187                 _index++;
56188                 return change(previous);
56189             }
56190
56191
56192             // internal _replace with eased time
56193             function _replace(args, t) {
56194                 var previous = _stack[_index].graph;
56195                 // assert(_index == _stack.length - 1)
56196                 var actionResult = _act(args, t);
56197                 _stack[_index] = actionResult;
56198                 return change(previous);
56199             }
56200
56201
56202             // internal _overwrite with eased time
56203             function _overwrite(args, t) {
56204                 var previous = _stack[_index].graph;
56205                 if (_index > 0) {
56206                     _index--;
56207                     _stack.pop();
56208                 }
56209                 _stack = _stack.slice(0, _index + 1);
56210                 var actionResult = _act(args, t);
56211                 _stack.push(actionResult);
56212                 _index++;
56213                 return change(previous);
56214             }
56215
56216
56217             // determine difference and dispatch a change event
56218             function change(previous) {
56219                 var difference = coreDifference(previous, history.graph());
56220                 if (!_pausedGraph) {
56221                     dispatch$1.call('change', this, difference);
56222                 }
56223                 return difference;
56224             }
56225
56226
56227             // iD uses namespaced keys so multiple installations do not conflict
56228             function getKey(n) {
56229                 return 'iD_' + window.location.origin + '_' + n;
56230             }
56231
56232
56233             var history = {
56234
56235                 graph: function() {
56236                     return _stack[_index].graph;
56237                 },
56238
56239
56240                 tree: function() {
56241                     return _tree;
56242                 },
56243
56244
56245                 base: function() {
56246                     return _stack[0].graph;
56247                 },
56248
56249
56250                 merge: function(entities/*, extent*/) {
56251                     var stack = _stack.map(function(state) { return state.graph; });
56252                     _stack[0].graph.rebase(entities, stack, false);
56253                     _tree.rebase(entities, false);
56254
56255                     dispatch$1.call('merge', this, entities);
56256                 },
56257
56258
56259                 perform: function() {
56260                     // complete any transition already in progress
56261                     select(document).interrupt('history.perform');
56262
56263                     var transitionable = false;
56264                     var action0 = arguments[0];
56265
56266                     if (arguments.length === 1 ||
56267                         (arguments.length === 2 && (typeof arguments[1] !== 'function'))) {
56268                         transitionable = !!action0.transitionable;
56269                     }
56270
56271                     if (transitionable) {
56272                         var origArguments = arguments;
56273                         select(document)
56274                             .transition('history.perform')
56275                             .duration(duration)
56276                             .ease(linear$1)
56277                             .tween('history.tween', function() {
56278                                 return function(t) {
56279                                     if (t < 1) { _overwrite([action0], t); }
56280                                 };
56281                             })
56282                             .on('start', function() {
56283                                 _perform([action0], 0);
56284                             })
56285                             .on('end interrupt', function() {
56286                                 _overwrite(origArguments, 1);
56287                             });
56288
56289                     } else {
56290                         return _perform(arguments);
56291                     }
56292                 },
56293
56294
56295                 replace: function() {
56296                     select(document).interrupt('history.perform');
56297                     return _replace(arguments, 1);
56298                 },
56299
56300
56301                 // Same as calling pop and then perform
56302                 overwrite: function() {
56303                     select(document).interrupt('history.perform');
56304                     return _overwrite(arguments, 1);
56305                 },
56306
56307
56308                 pop: function(n) {
56309                     select(document).interrupt('history.perform');
56310
56311                     var previous = _stack[_index].graph;
56312                     if (isNaN(+n) || +n < 0) {
56313                         n = 1;
56314                     }
56315                     while (n-- > 0 && _index > 0) {
56316                         _index--;
56317                         _stack.pop();
56318                     }
56319                     return change(previous);
56320                 },
56321
56322
56323                 // Back to the previous annotated state or _index = 0.
56324                 undo: function() {
56325                     select(document).interrupt('history.perform');
56326
56327                     var previousStack = _stack[_index];
56328                     var previous = previousStack.graph;
56329                     while (_index > 0) {
56330                         _index--;
56331                         if (_stack[_index].annotation) { break; }
56332                     }
56333
56334                     dispatch$1.call('undone', this, _stack[_index], previousStack);
56335                     return change(previous);
56336                 },
56337
56338
56339                 // Forward to the next annotated state.
56340                 redo: function() {
56341                     select(document).interrupt('history.perform');
56342
56343                     var previousStack = _stack[_index];
56344                     var previous = previousStack.graph;
56345                     var tryIndex = _index;
56346                     while (tryIndex < _stack.length - 1) {
56347                         tryIndex++;
56348                         if (_stack[tryIndex].annotation) {
56349                             _index = tryIndex;
56350                             dispatch$1.call('redone', this, _stack[_index], previousStack);
56351                             break;
56352                         }
56353                     }
56354
56355                     return change(previous);
56356                 },
56357
56358
56359                 pauseChangeDispatch: function() {
56360                     if (!_pausedGraph) {
56361                         _pausedGraph = _stack[_index].graph;
56362                     }
56363                 },
56364
56365
56366                 resumeChangeDispatch: function() {
56367                     if (_pausedGraph) {
56368                         var previous = _pausedGraph;
56369                         _pausedGraph = null;
56370                         return change(previous);
56371                     }
56372                 },
56373
56374
56375                 undoAnnotation: function() {
56376                     var i = _index;
56377                     while (i >= 0) {
56378                         if (_stack[i].annotation) { return _stack[i].annotation; }
56379                         i--;
56380                     }
56381                 },
56382
56383
56384                 redoAnnotation: function() {
56385                     var i = _index + 1;
56386                     while (i <= _stack.length - 1) {
56387                         if (_stack[i].annotation) { return _stack[i].annotation; }
56388                         i++;
56389                     }
56390                 },
56391
56392
56393                 // Returns the entities from the active graph with bounding boxes
56394                 // overlapping the given `extent`.
56395                 intersects: function(extent) {
56396                     return _tree.intersects(extent, _stack[_index].graph);
56397                 },
56398
56399
56400                 difference: function() {
56401                     var base = _stack[0].graph;
56402                     var head = _stack[_index].graph;
56403                     return coreDifference(base, head);
56404                 },
56405
56406
56407                 changes: function(action) {
56408                     var base = _stack[0].graph;
56409                     var head = _stack[_index].graph;
56410
56411                     if (action) {
56412                         head = action(head);
56413                     }
56414
56415                     var difference = coreDifference(base, head);
56416
56417                     return {
56418                         modified: difference.modified(),
56419                         created: difference.created(),
56420                         deleted: difference.deleted()
56421                     };
56422                 },
56423
56424
56425                 hasChanges: function() {
56426                     return this.difference().length() > 0;
56427                 },
56428
56429
56430                 imageryUsed: function(sources) {
56431                     if (sources) {
56432                         _imageryUsed = sources;
56433                         return history;
56434                     } else {
56435                         var s = new Set();
56436                         _stack.slice(1, _index + 1).forEach(function(state) {
56437                             state.imageryUsed.forEach(function(source) {
56438                                 if (source !== 'Custom') {
56439                                     s.add(source);
56440                                 }
56441                             });
56442                         });
56443                         return Array.from(s);
56444                     }
56445                 },
56446
56447
56448                 photoOverlaysUsed: function(sources) {
56449                     if (sources) {
56450                         _photoOverlaysUsed = sources;
56451                         return history;
56452                     } else {
56453                         var s = new Set();
56454                         _stack.slice(1, _index + 1).forEach(function(state) {
56455                             if (state.photoOverlaysUsed && Array.isArray(state.photoOverlaysUsed)) {
56456                                 state.photoOverlaysUsed.forEach(function(photoOverlay) {
56457                                     s.add(photoOverlay);
56458                                 });
56459                             }
56460                         });
56461                         return Array.from(s);
56462                     }
56463                 },
56464
56465
56466                 // save the current history state
56467                 checkpoint: function(key) {
56468                     _checkpoints[key] = {
56469                         stack: _stack,
56470                         index: _index
56471                     };
56472                     return history;
56473                 },
56474
56475
56476                 // restore history state to a given checkpoint or reset completely
56477                 reset: function(key) {
56478                     if (key !== undefined && _checkpoints.hasOwnProperty(key)) {
56479                         _stack = _checkpoints[key].stack;
56480                         _index = _checkpoints[key].index;
56481                     } else {
56482                         _stack = [{graph: coreGraph()}];
56483                         _index = 0;
56484                         _tree = coreTree(_stack[0].graph);
56485                         _checkpoints = {};
56486                     }
56487                     dispatch$1.call('change');
56488                     return history;
56489                 },
56490
56491
56492                 // `toIntroGraph()` is used to export the intro graph used by the walkthrough.
56493                 //
56494                 // To use it:
56495                 //  1. Start the walkthrough.
56496                 //  2. Get to a "free editing" tutorial step
56497                 //  3. Make your edits to the walkthrough map
56498                 //  4. In your browser dev console run:
56499                 //        `id.history().toIntroGraph()`
56500                 //  5. This outputs stringified JSON to the browser console
56501                 //  6. Copy it to `data/intro_graph.json` and prettify it in your code editor
56502                 toIntroGraph: function() {
56503                     var nextID = { n: 0, r: 0, w: 0 };
56504                     var permIDs = {};
56505                     var graph = this.graph();
56506                     var baseEntities = {};
56507
56508                     // clone base entities..
56509                     Object.values(graph.base().entities).forEach(function(entity) {
56510                         var copy = copyIntroEntity(entity);
56511                         baseEntities[copy.id] = copy;
56512                     });
56513
56514                     // replace base entities with head entities..
56515                     Object.keys(graph.entities).forEach(function(id) {
56516                         var entity = graph.entities[id];
56517                         if (entity) {
56518                             var copy = copyIntroEntity(entity);
56519                             baseEntities[copy.id] = copy;
56520                         } else {
56521                             delete baseEntities[id];
56522                         }
56523                     });
56524
56525                     // swap temporary for permanent ids..
56526                     Object.values(baseEntities).forEach(function(entity) {
56527                         if (Array.isArray(entity.nodes)) {
56528                             entity.nodes = entity.nodes.map(function(node) {
56529                                 return permIDs[node] || node;
56530                             });
56531                         }
56532                         if (Array.isArray(entity.members)) {
56533                             entity.members = entity.members.map(function(member) {
56534                                 member.id = permIDs[member.id] || member.id;
56535                                 return member;
56536                             });
56537                         }
56538                     });
56539
56540                     return JSON.stringify({ dataIntroGraph: baseEntities });
56541
56542
56543                     function copyIntroEntity(source) {
56544                         var copy = utilObjectOmit(source, ['type', 'user', 'v', 'version', 'visible']);
56545
56546                         // Note: the copy is no longer an osmEntity, so it might not have `tags`
56547                         if (copy.tags && !Object.keys(copy.tags)) {
56548                             delete copy.tags;
56549                         }
56550
56551                         if (Array.isArray(copy.loc)) {
56552                             copy.loc[0] = +copy.loc[0].toFixed(6);
56553                             copy.loc[1] = +copy.loc[1].toFixed(6);
56554                         }
56555
56556                         var match = source.id.match(/([nrw])-\d*/);  // temporary id
56557                         if (match !== null) {
56558                             var nrw = match[1];
56559                             var permID;
56560                             do { permID = nrw + (++nextID[nrw]); }
56561                             while (baseEntities.hasOwnProperty(permID));
56562
56563                             copy.id = permIDs[source.id] = permID;
56564                         }
56565                         return copy;
56566                     }
56567                 },
56568
56569
56570                 toJSON: function() {
56571                     if (!this.hasChanges()) { return; }
56572
56573                     var allEntities = {};
56574                     var baseEntities = {};
56575                     var base = _stack[0];
56576
56577                     var s = _stack.map(function(i) {
56578                         var modified = [];
56579                         var deleted = [];
56580
56581                         Object.keys(i.graph.entities).forEach(function(id) {
56582                             var entity = i.graph.entities[id];
56583                             if (entity) {
56584                                 var key = osmEntity.key(entity);
56585                                 allEntities[key] = entity;
56586                                 modified.push(key);
56587                             } else {
56588                                 deleted.push(id);
56589                             }
56590
56591                             // make sure that the originals of changed or deleted entities get merged
56592                             // into the base of the _stack after restoring the data from JSON.
56593                             if (id in base.graph.entities) {
56594                                 baseEntities[id] = base.graph.entities[id];
56595                             }
56596                             if (entity && entity.nodes) {
56597                                 // get originals of pre-existing child nodes
56598                                 entity.nodes.forEach(function(nodeID) {
56599                                     if (nodeID in base.graph.entities) {
56600                                         baseEntities[nodeID] = base.graph.entities[nodeID];
56601                                     }
56602                                 });
56603                             }
56604                             // get originals of parent entities too
56605                             var baseParents = base.graph._parentWays[id];
56606                             if (baseParents) {
56607                                 baseParents.forEach(function(parentID) {
56608                                     if (parentID in base.graph.entities) {
56609                                         baseEntities[parentID] = base.graph.entities[parentID];
56610                                     }
56611                                 });
56612                             }
56613                         });
56614
56615                         var x = {};
56616
56617                         if (modified.length) { x.modified = modified; }
56618                         if (deleted.length) { x.deleted = deleted; }
56619                         if (i.imageryUsed) { x.imageryUsed = i.imageryUsed; }
56620                         if (i.photoOverlaysUsed) { x.photoOverlaysUsed = i.photoOverlaysUsed; }
56621                         if (i.annotation) { x.annotation = i.annotation; }
56622                         if (i.transform) { x.transform = i.transform; }
56623                         if (i.selectedIDs) { x.selectedIDs = i.selectedIDs; }
56624
56625                         return x;
56626                     });
56627
56628                     return JSON.stringify({
56629                         version: 3,
56630                         entities: Object.values(allEntities),
56631                         baseEntities: Object.values(baseEntities),
56632                         stack: s,
56633                         nextIDs: osmEntity.id.next,
56634                         index: _index,
56635                         // note the time the changes were saved
56636                         timestamp: (new Date()).getTime()
56637                     });
56638                 },
56639
56640
56641                 fromJSON: function(json, loadChildNodes) {
56642                     var h = JSON.parse(json);
56643                     var loadComplete = true;
56644
56645                     osmEntity.id.next = h.nextIDs;
56646                     _index = h.index;
56647
56648                     if (h.version === 2 || h.version === 3) {
56649                         var allEntities = {};
56650
56651                         h.entities.forEach(function(entity) {
56652                             allEntities[osmEntity.key(entity)] = osmEntity(entity);
56653                         });
56654
56655                         if (h.version === 3) {
56656                             // This merges originals for changed entities into the base of
56657                             // the _stack even if the current _stack doesn't have them (for
56658                             // example when iD has been restarted in a different region)
56659                             var baseEntities = h.baseEntities.map(function(d) { return osmEntity(d); });
56660                             var stack = _stack.map(function(state) { return state.graph; });
56661                             _stack[0].graph.rebase(baseEntities, stack, true);
56662                             _tree.rebase(baseEntities, true);
56663
56664                             // When we restore a modified way, we also need to fetch any missing
56665                             // childnodes that would normally have been downloaded with it.. #2142
56666                             if (loadChildNodes) {
56667                                 var osm = context.connection();
56668                                 var baseWays = baseEntities
56669                                     .filter(function(e) { return e.type === 'way'; });
56670                                 var nodeIDs = baseWays
56671                                     .reduce(function(acc, way) { return utilArrayUnion(acc, way.nodes); }, []);
56672                                 var missing = nodeIDs
56673                                     .filter(function(n) { return !_stack[0].graph.hasEntity(n); });
56674
56675                                 if (missing.length && osm) {
56676                                     loadComplete = false;
56677                                     context.map().redrawEnable(false);
56678
56679                                     var loading = uiLoading(context).blocking(true);
56680                                     context.container().call(loading);
56681
56682                                     var childNodesLoaded = function(err, result) {
56683                                         if (!err) {
56684                                             var visibleGroups = utilArrayGroupBy(result.data, 'visible');
56685                                             var visibles = visibleGroups.true || [];      // alive nodes
56686                                             var invisibles = visibleGroups.false || [];   // deleted nodes
56687
56688                                             if (visibles.length) {
56689                                                 var visibleIDs = visibles.map(function(entity) { return entity.id; });
56690                                                 var stack = _stack.map(function(state) { return state.graph; });
56691                                                 missing = utilArrayDifference(missing, visibleIDs);
56692                                                 _stack[0].graph.rebase(visibles, stack, true);
56693                                                 _tree.rebase(visibles, true);
56694                                             }
56695
56696                                             // fetch older versions of nodes that were deleted..
56697                                             invisibles.forEach(function(entity) {
56698                                                 osm.loadEntityVersion(entity.id, +entity.version - 1, childNodesLoaded);
56699                                             });
56700                                         }
56701
56702                                         if (err || !missing.length) {
56703                                             loading.close();
56704                                             context.map().redrawEnable(true);
56705                                             dispatch$1.call('change');
56706                                             dispatch$1.call('restore', this);
56707                                         }
56708                                     };
56709
56710                                     osm.loadMultiple(missing, childNodesLoaded);
56711                                 }
56712                             }
56713                         }
56714
56715                         _stack = h.stack.map(function(d) {
56716                             var entities = {}, entity;
56717
56718                             if (d.modified) {
56719                                 d.modified.forEach(function(key) {
56720                                     entity = allEntities[key];
56721                                     entities[entity.id] = entity;
56722                                 });
56723                             }
56724
56725                             if (d.deleted) {
56726                                 d.deleted.forEach(function(id) {
56727                                     entities[id] = undefined;
56728                                 });
56729                             }
56730
56731                             return {
56732                                 graph: coreGraph(_stack[0].graph).load(entities),
56733                                 annotation: d.annotation,
56734                                 imageryUsed: d.imageryUsed,
56735                                 photoOverlaysUsed: d.photoOverlaysUsed,
56736                                 transform: d.transform,
56737                                 selectedIDs: d.selectedIDs
56738                             };
56739                         });
56740
56741                     } else { // original version
56742                         _stack = h.stack.map(function(d) {
56743                             var entities = {};
56744
56745                             for (var i in d.entities) {
56746                                 var entity = d.entities[i];
56747                                 entities[i] = entity === 'undefined' ? undefined : osmEntity(entity);
56748                             }
56749
56750                             d.graph = coreGraph(_stack[0].graph).load(entities);
56751                             return d;
56752                         });
56753                     }
56754
56755                     var transform = _stack[_index].transform;
56756                     if (transform) {
56757                         context.map().transformEase(transform, 0);   // 0 = immediate, no easing
56758                     }
56759
56760                     if (loadComplete) {
56761                         dispatch$1.call('change');
56762                         dispatch$1.call('restore', this);
56763                     }
56764
56765                     return history;
56766                 },
56767
56768
56769                 lock: function() {
56770                     return lock.lock();
56771                 },
56772
56773
56774                 unlock: function() {
56775                     lock.unlock();
56776                 },
56777
56778
56779                 save: function() {
56780                     if (lock.locked() &&
56781                         // don't overwrite existing, unresolved changes
56782                         !_hasUnresolvedRestorableChanges) {
56783
56784                         corePreferences(getKey('saved_history'), history.toJSON() || null);
56785                     }
56786                     return history;
56787                 },
56788
56789
56790                 // delete the history version saved in localStorage
56791                 clearSaved: function() {
56792                     context.debouncedSave.cancel();
56793                     if (lock.locked()) {
56794                         _hasUnresolvedRestorableChanges = false;
56795                         corePreferences(getKey('saved_history'), null);
56796
56797                         // clear the changeset metadata associated with the saved history
56798                         corePreferences('comment', null);
56799                         corePreferences('hashtags', null);
56800                         corePreferences('source', null);
56801                     }
56802                     return history;
56803                 },
56804
56805
56806                 savedHistoryJSON: function() {
56807                     return corePreferences(getKey('saved_history'));
56808                 },
56809
56810
56811                 hasRestorableChanges: function() {
56812                     return _hasUnresolvedRestorableChanges;
56813                 },
56814
56815
56816                 // load history from a version stored in localStorage
56817                 restore: function() {
56818                     if (lock.locked()) {
56819                         _hasUnresolvedRestorableChanges = false;
56820                         var json = this.savedHistoryJSON();
56821                         if (json) { history.fromJSON(json, true); }
56822                     }
56823                 },
56824
56825
56826                 _getKey: getKey
56827
56828             };
56829
56830
56831             history.reset();
56832
56833             return utilRebind(history, dispatch$1, 'on');
56834         }
56835
56836         /**
56837          * Look for roads that can be connected to other roads with a short extension
56838          */
56839         function validationAlmostJunction(context) {
56840           var type = 'almost_junction';
56841           var EXTEND_TH_METERS = 5;
56842           var WELD_TH_METERS = 0.75;
56843           // Comes from considering bounding case of parallel ways
56844           var CLOSE_NODE_TH = EXTEND_TH_METERS - WELD_TH_METERS;
56845           // Comes from considering bounding case of perpendicular ways
56846           var SIG_ANGLE_TH = Math.atan(WELD_TH_METERS / EXTEND_TH_METERS);
56847
56848           function isHighway(entity) {
56849             return entity.type === 'way'
56850               && osmRoutableHighwayTagValues[entity.tags.highway];
56851           }
56852
56853           function isTaggedAsNotContinuing(node) {
56854             return node.tags.noexit === 'yes'
56855               || node.tags.amenity === 'parking_entrance'
56856               || (node.tags.entrance && node.tags.entrance !== 'no');
56857           }
56858
56859
56860           var validation = function checkAlmostJunction(entity, graph) {
56861             if (!isHighway(entity)) { return []; }
56862             if (entity.isDegenerate()) { return []; }
56863
56864             var tree = context.history().tree();
56865             var extendableNodeInfos = findConnectableEndNodesByExtension(entity);
56866
56867             var issues = [];
56868
56869             extendableNodeInfos.forEach(function (extendableNodeInfo) {
56870               issues.push(new validationIssue({
56871                 type: type,
56872                 subtype: 'highway-highway',
56873                 severity: 'warning',
56874                 message: function message(context) {
56875                   var entity1 = context.hasEntity(this.entityIds[0]);
56876                   if (this.entityIds[0] === this.entityIds[2]) {
56877                     return entity1 ? _t('issues.almost_junction.self.message', {
56878                       feature: utilDisplayLabel(entity1, context.graph())
56879                     }) : '';
56880                   } else {
56881                     var entity2 = context.hasEntity(this.entityIds[2]);
56882                     return (entity1 && entity2) ? _t('issues.almost_junction.message', {
56883                       feature: utilDisplayLabel(entity1, context.graph()),
56884                       feature2: utilDisplayLabel(entity2, context.graph())
56885                     }) : '';
56886                   }
56887                 },
56888                 reference: showReference,
56889                 entityIds: [
56890                   entity.id,
56891                   extendableNodeInfo.node.id,
56892                   extendableNodeInfo.wid ],
56893                 loc: extendableNodeInfo.node.loc,
56894                 hash: JSON.stringify(extendableNodeInfo.node.loc),
56895                 data: {
56896                   midId: extendableNodeInfo.mid.id,
56897                   edge: extendableNodeInfo.edge,
56898                   cross_loc: extendableNodeInfo.cross_loc
56899                 },
56900                 dynamicFixes: makeFixes
56901               }));
56902             });
56903
56904             return issues;
56905
56906             function makeFixes(context) {
56907               var fixes = [new validationIssueFix({
56908                 icon: 'iD-icon-abutment',
56909                 title: _t('issues.fix.connect_features.title'),
56910                 onClick: function onClick(context) {
56911                   var annotation = _t('issues.fix.connect_almost_junction.annotation');
56912                   var ref = this.issue.entityIds;
56913                   var endNodeId = ref[1];
56914                   var crossWayId = ref[2];
56915                   var midNode = context.entity(this.issue.data.midId);
56916                   var endNode = context.entity(endNodeId);
56917                   var crossWay = context.entity(crossWayId);
56918
56919                   // When endpoints are close, just join if resulting small change in angle (#7201)
56920                   var nearEndNodes = findNearbyEndNodes(endNode, crossWay);
56921                   if (nearEndNodes.length > 0) {
56922                     var collinear = findSmallJoinAngle(midNode, endNode, nearEndNodes);
56923                     if (collinear) {
56924                       context.perform(
56925                         actionMergeNodes([collinear.id, endNode.id], collinear.loc),
56926                         annotation
56927                       );
56928                       return;
56929                     }
56930                   }
56931
56932                   var targetEdge = this.issue.data.edge;
56933                   var crossLoc = this.issue.data.cross_loc;
56934                   var edgeNodes = [context.entity(targetEdge[0]), context.entity(targetEdge[1])];
56935                   var closestNodeInfo = geoSphericalClosestNode(edgeNodes, crossLoc);
56936
56937                   // already a point nearby, just connect to that
56938                   if (closestNodeInfo.distance < WELD_TH_METERS) {
56939                     context.perform(
56940                       actionMergeNodes([closestNodeInfo.node.id, endNode.id], closestNodeInfo.node.loc),
56941                       annotation
56942                     );
56943                   // else add the end node to the edge way
56944                   } else {
56945                     context.perform(
56946                       actionAddMidpoint({loc: crossLoc, edge: targetEdge}, endNode),
56947                       annotation
56948                     );
56949                   }
56950                 }
56951               })];
56952
56953               var node = context.hasEntity(this.entityIds[1]);
56954               if (node && !node.hasInterestingTags()) {
56955                 // node has no descriptive tags, suggest noexit fix
56956                 fixes.push(new validationIssueFix({
56957                   icon: 'maki-barrier',
56958                   title: _t('issues.fix.tag_as_disconnected.title'),
56959                   onClick: function onClick(context) {
56960                     var nodeID = this.issue.entityIds[1];
56961                     var tags = Object.assign({}, context.entity(nodeID).tags);
56962                     tags.noexit = 'yes';
56963                     context.perform(
56964                       actionChangeTags(nodeID, tags),
56965                       _t('issues.fix.tag_as_disconnected.annotation')
56966                     );
56967                   }
56968                 }));
56969               }
56970
56971               return fixes;
56972             }
56973
56974             function showReference(selection) {
56975               selection.selectAll('.issue-reference')
56976                 .data([0])
56977                 .enter()
56978                 .append('div')
56979                 .attr('class', 'issue-reference')
56980                 .text(_t('issues.almost_junction.highway-highway.reference'));
56981             }
56982
56983             function isExtendableCandidate(node, way) {
56984               // can not accurately test vertices on tiles not downloaded from osm - #5938
56985               var osm = services.osm;
56986               if (osm && !osm.isDataLoaded(node.loc)) {
56987                 return false;
56988               }
56989               if (isTaggedAsNotContinuing(node) || graph.parentWays(node).length !== 1) {
56990                 return false;
56991               }
56992
56993               var occurences = 0;
56994               for (var index in way.nodes) {
56995                 if (way.nodes[index] === node.id) {
56996                   occurences += 1;
56997                   if (occurences > 1) {
56998                     return false;
56999                   }
57000                 }
57001               }
57002               return true;
57003             }
57004
57005             function findConnectableEndNodesByExtension(way) {
57006               var results = [];
57007               if (way.isClosed()) { return results; }
57008
57009               var testNodes;
57010               var indices = [0, way.nodes.length - 1];
57011               indices.forEach(function (nodeIndex) {
57012                 var nodeID = way.nodes[nodeIndex];
57013                 var node = graph.entity(nodeID);
57014
57015                 if (!isExtendableCandidate(node, way)) { return; }
57016
57017                 var connectionInfo = canConnectByExtend(way, nodeIndex);
57018                 if (!connectionInfo) { return; }
57019
57020                 testNodes = graph.childNodes(way).slice();   // shallow copy
57021                 testNodes[nodeIndex] = testNodes[nodeIndex].move(connectionInfo.cross_loc);
57022
57023                 // don't flag issue if connecting the ways would cause self-intersection
57024                 if (geoHasSelfIntersections(testNodes, nodeID)) { return; }
57025
57026                 results.push(connectionInfo);
57027               });
57028
57029               return results;
57030             }
57031
57032             function findNearbyEndNodes(node, way) {
57033               return [
57034                 way.nodes[0],
57035                 way.nodes[way.nodes.length - 1]
57036               ].map(function (d) { return graph.entity(d); })
57037               .filter(function (d) {
57038                 // Node cannot be near to itself, but other endnode of same way could be
57039                 return d.id !== node.id
57040                   && geoSphericalDistance(node.loc, d.loc) <= CLOSE_NODE_TH;
57041               });
57042             }
57043
57044             function findSmallJoinAngle(midNode, tipNode, endNodes) {
57045               // Both nodes could be close, so want to join whichever is closest to collinear
57046               var joinTo;
57047               var minAngle = Infinity;
57048
57049               // Checks midNode -> tipNode -> endNode for collinearity
57050               endNodes.forEach(function (endNode) {
57051                 var a1 = geoAngle(midNode, tipNode, context.projection) + Math.PI;
57052                 var a2 = geoAngle(midNode, endNode, context.projection) + Math.PI;
57053                 var diff = Math.max(a1, a2) - Math.min(a1, a2);
57054
57055                 if (diff < minAngle) {
57056                   joinTo = endNode;
57057                   minAngle = diff;
57058                 }
57059               });
57060
57061               /* Threshold set by considering right angle triangle
57062               based on node joining threshold and extension distance */
57063               if (minAngle <= SIG_ANGLE_TH) { return joinTo; }
57064
57065               return null;
57066             }
57067
57068             function hasTag(tags, key) {
57069               return tags[key] !== undefined && tags[key] !== 'no';
57070             }
57071
57072             function canConnectWays(way, way2) {
57073
57074               // allow self-connections
57075               if (way.id === way2.id) { return true; }
57076
57077               // if one is bridge or tunnel, both must be bridge or tunnel
57078               if ((hasTag(way.tags, 'bridge') || hasTag(way2.tags, 'bridge')) &&
57079                 !(hasTag(way.tags, 'bridge') && hasTag(way2.tags, 'bridge'))) { return false; }
57080               if ((hasTag(way.tags, 'tunnel') || hasTag(way2.tags, 'tunnel')) &&
57081                 !(hasTag(way.tags, 'tunnel') && hasTag(way2.tags, 'tunnel'))) { return false; }
57082
57083               // must have equivalent layers and levels
57084               var layer1 = way.tags.layer || '0',
57085                 layer2 = way2.tags.layer || '0';
57086               if (layer1 !== layer2) { return false; }
57087
57088               var level1 = way.tags.level || '0',
57089                 level2 = way2.tags.level || '0';
57090               if (level1 !== level2) { return false; }
57091
57092               return true;
57093             }
57094
57095             function canConnectByExtend(way, endNodeIdx) {
57096               var tipNid = way.nodes[endNodeIdx];  // the 'tip' node for extension point
57097               var midNid = endNodeIdx === 0 ? way.nodes[1] : way.nodes[way.nodes.length - 2];  // the other node of the edge
57098               var tipNode = graph.entity(tipNid);
57099               var midNode = graph.entity(midNid);
57100               var lon = tipNode.loc[0];
57101               var lat = tipNode.loc[1];
57102               var lon_range = geoMetersToLon(EXTEND_TH_METERS, lat) / 2;
57103               var lat_range = geoMetersToLat(EXTEND_TH_METERS) / 2;
57104               var queryExtent = geoExtent([
57105                 [lon - lon_range, lat - lat_range],
57106                 [lon + lon_range, lat + lat_range]
57107               ]);
57108
57109               // first, extend the edge of [midNode -> tipNode] by EXTEND_TH_METERS and find the "extended tip" location
57110               var edgeLen = geoSphericalDistance(midNode.loc, tipNode.loc);
57111               var t = EXTEND_TH_METERS / edgeLen + 1.0;
57112               var extTipLoc = geoVecInterp(midNode.loc, tipNode.loc, t);
57113
57114               // then, check if the extension part [tipNode.loc -> extTipLoc] intersects any other ways
57115               var segmentInfos = tree.waySegments(queryExtent, graph);
57116               for (var i = 0; i < segmentInfos.length; i++) {
57117                 var segmentInfo = segmentInfos[i];
57118
57119                 var way2 = graph.entity(segmentInfo.wayId);
57120
57121                 if (!isHighway(way2)) { continue; }
57122
57123                 if (!canConnectWays(way, way2)) { continue; }
57124
57125                 var nAid = segmentInfo.nodes[0],
57126                   nBid = segmentInfo.nodes[1];
57127
57128                 if (nAid === tipNid || nBid === tipNid) { continue; }
57129
57130                 var nA = graph.entity(nAid),
57131                   nB = graph.entity(nBid);
57132                 var crossLoc = geoLineIntersection([tipNode.loc, extTipLoc], [nA.loc, nB.loc]);
57133                 if (crossLoc) {
57134                   return {
57135                     mid: midNode,
57136                     node: tipNode,
57137                     wid: way2.id,
57138                     edge: [nA.id, nB.id],
57139                     cross_loc: crossLoc
57140                   };
57141                 }
57142               }
57143               return null;
57144             }
57145           };
57146
57147           validation.type = type;
57148
57149           return validation;
57150         }
57151
57152         function validationCloseNodes(context) {
57153             var type = 'close_nodes';
57154
57155             var pointThresholdMeters = 0.2;
57156
57157             var validation = function(entity, graph) {
57158                 if (entity.type === 'node') {
57159                     return getIssuesForNode(entity);
57160                 } else if (entity.type === 'way') {
57161                     return getIssuesForWay(entity);
57162                 }
57163                 return [];
57164
57165                 function getIssuesForNode(node) {
57166                     var parentWays = graph.parentWays(node);
57167                     if (parentWays.length) {
57168                         return getIssuesForVertex(node, parentWays);
57169                     } else {
57170                         return getIssuesForDetachedPoint(node);
57171                     }
57172                 }
57173
57174                 function wayTypeFor(way) {
57175
57176                     if (way.tags.boundary && way.tags.boundary !== 'no') { return 'boundary'; }
57177                     if (way.tags.indoor && way.tags.indoor !== 'no') { return 'indoor'; }
57178                     if ((way.tags.building && way.tags.building !== 'no') ||
57179                         (way.tags['building:part'] && way.tags['building:part'] !== 'no')) { return 'building'; }
57180                     if (osmPathHighwayTagValues[way.tags.highway]) { return 'path'; }
57181
57182                     var parentRelations = graph.parentRelations(way);
57183                     for (var i in parentRelations) {
57184                         var relation = parentRelations[i];
57185
57186                         if (relation.tags.type === 'boundary') { return 'boundary'; }
57187
57188                         if (relation.isMultipolygon()) {
57189                             if (relation.tags.indoor && relation.tags.indoor !== 'no') { return 'indoor'; }
57190                             if ((relation.tags.building && relation.tags.building !== 'no') ||
57191                                 (relation.tags['building:part'] && relation.tags['building:part'] !== 'no')) { return 'building'; }
57192                         }
57193                     }
57194
57195                     return 'other';
57196                 }
57197
57198                 function shouldCheckWay(way) {
57199
57200                     // don't flag issues where merging would create degenerate ways
57201                     if (way.nodes.length <= 2 ||
57202                         (way.isClosed() && way.nodes.length <= 4)) { return false; }
57203
57204                     var bbox = way.extent(graph).bbox();
57205                     var hypotenuseMeters = geoSphericalDistance([bbox.minX, bbox.minY], [bbox.maxX, bbox.maxY]);
57206                     // don't flag close nodes in very small ways
57207                     if (hypotenuseMeters < 1.5) { return false; }
57208
57209                     return true;
57210                 }
57211
57212                 function getIssuesForWay(way) {
57213                     if (!shouldCheckWay(way)) { return []; }
57214
57215                     var issues = [],
57216                         nodes = graph.childNodes(way);
57217                     for (var i = 0; i < nodes.length - 1; i++) {
57218                         var node1 = nodes[i];
57219                         var node2 = nodes[i+1];
57220
57221                         var issue = getWayIssueIfAny(node1, node2, way);
57222                         if (issue) { issues.push(issue); }
57223                     }
57224                     return issues;
57225                 }
57226
57227                 function getIssuesForVertex(node, parentWays) {
57228                     var issues = [];
57229
57230                     function checkForCloseness(node1, node2, way) {
57231                         var issue = getWayIssueIfAny(node1, node2, way);
57232                         if (issue) { issues.push(issue); }
57233                     }
57234
57235                     for (var i = 0; i < parentWays.length; i++) {
57236                         var parentWay = parentWays[i];
57237
57238                         if (!shouldCheckWay(parentWay)) { continue; }
57239
57240                         var lastIndex = parentWay.nodes.length - 1;
57241                         for (var j = 0; j < parentWay.nodes.length; j++) {
57242                             if (j !== 0) {
57243                                 if (parentWay.nodes[j-1] === node.id) {
57244                                     checkForCloseness(node, graph.entity(parentWay.nodes[j]), parentWay);
57245                                 }
57246                             }
57247                             if (j !== lastIndex) {
57248                                 if (parentWay.nodes[j+1] === node.id) {
57249                                     checkForCloseness(graph.entity(parentWay.nodes[j]), node, parentWay);
57250                                 }
57251                             }
57252                         }
57253                     }
57254                     return issues;
57255                 }
57256
57257                 function thresholdMetersForWay(way) {
57258                     if (!shouldCheckWay(way)) { return 0; }
57259
57260                     var wayType = wayTypeFor(way);
57261
57262                     // don't flag boundaries since they might be highly detailed and can't be easily verified
57263                     if (wayType === 'boundary') { return 0; }
57264                     // expect some features to be mapped with higher levels of detail
57265                     if (wayType === 'indoor') { return 0.01; }
57266                     if (wayType === 'building') { return 0.05; }
57267                     if (wayType === 'path') { return 0.1; }
57268                     return 0.2;
57269                 }
57270
57271                 function getIssuesForDetachedPoint(node) {
57272
57273                     var issues = [];
57274
57275                     var lon = node.loc[0];
57276                     var lat = node.loc[1];
57277                     var lon_range = geoMetersToLon(pointThresholdMeters, lat) / 2;
57278                     var lat_range = geoMetersToLat(pointThresholdMeters) / 2;
57279                     var queryExtent = geoExtent([
57280                         [lon - lon_range, lat - lat_range],
57281                         [lon + lon_range, lat + lat_range]
57282                     ]);
57283
57284                     var intersected = context.history().tree().intersects(queryExtent, graph);
57285                     for (var j = 0; j < intersected.length; j++) {
57286                         var nearby = intersected[j];
57287
57288                         if (nearby.id === node.id) { continue; }
57289                         if (nearby.type !== 'node' || nearby.geometry(graph) !== 'point') { continue; }
57290
57291                         if (nearby.loc === node.loc ||
57292                             geoSphericalDistance(node.loc, nearby.loc) < pointThresholdMeters) {
57293
57294                             // allow very close points if tags indicate the z-axis might vary
57295                             var zAxisKeys = { layer: true, level: true, 'addr:housenumber': true, 'addr:unit': true };
57296                             var zAxisDifferentiates = false;
57297                             for (var key in zAxisKeys) {
57298                                 var nodeValue = node.tags[key] || '0';
57299                                 var nearbyValue = nearby.tags[key] || '0';
57300                                 if (nodeValue !== nearbyValue) {
57301                                     zAxisDifferentiates = true;
57302                                     break;
57303                                 }
57304                             }
57305                             if (zAxisDifferentiates) { continue; }
57306
57307                             issues.push(new validationIssue({
57308                                 type: type,
57309                                 subtype: 'detached',
57310                                 severity: 'warning',
57311                                 message: function(context) {
57312                                     var entity = context.hasEntity(this.entityIds[0]),
57313                                         entity2 = context.hasEntity(this.entityIds[1]);
57314                                     return (entity && entity2) ? _t('issues.close_nodes.detached.message', {
57315                                         feature: utilDisplayLabel(entity, context.graph()),
57316                                         feature2: utilDisplayLabel(entity2, context.graph())
57317                                     }) : '';
57318                                 },
57319                                 reference: showReference,
57320                                 entityIds: [node.id, nearby.id],
57321                                 dynamicFixes: function() {
57322                                     return [
57323                                         new validationIssueFix({
57324                                             icon: 'iD-operation-disconnect',
57325                                             title: _t('issues.fix.move_points_apart.title')
57326                                         }),
57327                                         new validationIssueFix({
57328                                             icon: 'iD-icon-layers',
57329                                             title: _t('issues.fix.use_different_layers_or_levels.title')
57330                                         })
57331                                     ];
57332                                 }
57333                             }));
57334                         }
57335                     }
57336
57337                     return issues;
57338
57339                     function showReference(selection) {
57340                         var referenceText = _t('issues.close_nodes.detached.reference');
57341                         selection.selectAll('.issue-reference')
57342                             .data([0])
57343                             .enter()
57344                             .append('div')
57345                             .attr('class', 'issue-reference')
57346                             .text(referenceText);
57347                     }
57348                 }
57349
57350                 function getWayIssueIfAny(node1, node2, way) {
57351                     if (node1.id === node2.id ||
57352                         (node1.hasInterestingTags() && node2.hasInterestingTags())) {
57353                         return null;
57354                     }
57355
57356                     if (node1.loc !== node2.loc) {
57357                         var parentWays1 = graph.parentWays(node1);
57358                         var parentWays2 = new Set(graph.parentWays(node2));
57359
57360                         var sharedWays = parentWays1.filter(function(parentWay) {
57361                             return parentWays2.has(parentWay);
57362                         });
57363
57364                         var thresholds = sharedWays.map(function(parentWay) {
57365                             return thresholdMetersForWay(parentWay);
57366                         });
57367
57368                         var threshold = Math.min.apply(Math, thresholds);
57369                         var distance = geoSphericalDistance(node1.loc, node2.loc);
57370                         if (distance > threshold) { return null; }
57371                     }
57372
57373                     return new validationIssue({
57374                         type: type,
57375                         subtype: 'vertices',
57376                         severity: 'warning',
57377                         message: function(context) {
57378                             var entity = context.hasEntity(this.entityIds[0]);
57379                             return entity ? _t('issues.close_nodes.message', { way: utilDisplayLabel(entity, context.graph()) }) : '';
57380                         },
57381                         reference: showReference,
57382                         entityIds: [way.id, node1.id, node2.id],
57383                         loc: node1.loc,
57384                         dynamicFixes: function() {
57385                             return [
57386                                 new validationIssueFix({
57387                                     icon: 'iD-icon-plus',
57388                                     title: _t('issues.fix.merge_points.title'),
57389                                     onClick: function(context) {
57390                                         var entityIds = this.issue.entityIds;
57391                                         var action = actionMergeNodes([entityIds[1], entityIds[2]]);
57392                                         context.perform(action, _t('issues.fix.merge_close_vertices.annotation'));
57393                                     }
57394                                 }),
57395                                 new validationIssueFix({
57396                                     icon: 'iD-operation-disconnect',
57397                                     title: _t('issues.fix.move_points_apart.title')
57398                                 })
57399                             ];
57400                         }
57401                     });
57402
57403                     function showReference(selection) {
57404                         var referenceText = _t('issues.close_nodes.reference');
57405                         selection.selectAll('.issue-reference')
57406                             .data([0])
57407                             .enter()
57408                             .append('div')
57409                             .attr('class', 'issue-reference')
57410                             .text(referenceText);
57411                     }
57412                 }
57413
57414             };
57415
57416
57417             validation.type = type;
57418
57419             return validation;
57420         }
57421
57422         function validationCrossingWays(context) {
57423             var type = 'crossing_ways';
57424
57425             // returns the way or its parent relation, whichever has a useful feature type
57426             function getFeatureWithFeatureTypeTagsForWay(way, graph) {
57427                 if (getFeatureType(way, graph) === null) {
57428                     // if the way doesn't match a feature type, check its parent relations
57429                     var parentRels = graph.parentRelations(way);
57430                     for (var i = 0; i < parentRels.length; i++) {
57431                         var rel = parentRels[i];
57432                         if (getFeatureType(rel, graph) !== null) {
57433                             return rel;
57434                         }
57435                     }
57436                 }
57437                 return way;
57438             }
57439
57440
57441             function hasTag(tags, key) {
57442                 return tags[key] !== undefined && tags[key] !== 'no';
57443             }
57444
57445             function taggedAsIndoor(tags) {
57446                 return hasTag(tags, 'indoor') ||
57447                     hasTag(tags, 'level') ||
57448                     tags.highway === 'corridor';
57449             }
57450
57451             function allowsBridge(featureType) {
57452                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57453             }
57454             function allowsTunnel(featureType) {
57455                 return featureType === 'highway' || featureType === 'railway' || featureType === 'waterway';
57456             }
57457
57458
57459             function getFeatureTypeForCrossingCheck(way, graph) {
57460                 var feature = getFeatureWithFeatureTypeTagsForWay(way, graph);
57461                 return getFeatureType(feature, graph);
57462             }
57463
57464             // discard
57465             var ignoredBuildings = {
57466                 demolished: true, dismantled: true, proposed: true, razed: true
57467             };
57468
57469
57470             function getFeatureType(entity, graph) {
57471
57472                 var geometry = entity.geometry(graph);
57473                 if (geometry !== 'line' && geometry !== 'area') { return null; }
57474
57475                 var tags = entity.tags;
57476
57477                 if (hasTag(tags, 'building') && !ignoredBuildings[tags.building]) { return 'building'; }
57478                 if (hasTag(tags, 'highway') && osmRoutableHighwayTagValues[tags.highway]) { return 'highway'; }
57479
57480                 // don't check railway or waterway areas
57481                 if (geometry !== 'line') { return null; }
57482
57483                 if (hasTag(tags, 'railway') && osmRailwayTrackTagValues[tags.railway]) { return 'railway'; }
57484                 if (hasTag(tags, 'waterway') && osmFlowingWaterwayTagValues[tags.waterway]) { return 'waterway'; }
57485
57486                 return null;
57487             }
57488
57489
57490             function isLegitCrossing(way1, featureType1, way2, featureType2) {
57491                 var tags1 = way1.tags;
57492                 var tags2 = way2.tags;
57493
57494                 // assume 0 by default
57495                 var level1 = tags1.level || '0';
57496                 var level2 = tags2.level || '0';
57497
57498                 if (taggedAsIndoor(tags1) && taggedAsIndoor(tags2) && level1 !== level2) {
57499                     // assume features don't interact if they're indoor on different levels
57500                     return true;
57501                 }
57502
57503                 // assume 0 by default; don't use way.layer() since we account for structures here
57504                 var layer1 = tags1.layer || '0';
57505                 var layer2 = tags2.layer || '0';
57506
57507                 if (allowsBridge(featureType1) && allowsBridge(featureType2)) {
57508                     if (hasTag(tags1, 'bridge') && !hasTag(tags2, 'bridge')) { return true; }
57509                     if (!hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge')) { return true; }
57510                     // crossing bridges must use different layers
57511                     if (hasTag(tags1, 'bridge') && hasTag(tags2, 'bridge') && layer1 !== layer2) { return true; }
57512                 } else if (allowsBridge(featureType1) && hasTag(tags1, 'bridge')) { return true; }
57513                 else if (allowsBridge(featureType2) && hasTag(tags2, 'bridge')) { return true; }
57514
57515                 if (allowsTunnel(featureType1) && allowsTunnel(featureType2)) {
57516                     if (hasTag(tags1, 'tunnel') && !hasTag(tags2, 'tunnel')) { return true; }
57517                     if (!hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel')) { return true; }
57518                     // crossing tunnels must use different layers
57519                     if (hasTag(tags1, 'tunnel') && hasTag(tags2, 'tunnel') && layer1 !== layer2) { return true; }
57520                 } else if (allowsTunnel(featureType1) && hasTag(tags1, 'tunnel')) { return true; }
57521                 else if (allowsTunnel(featureType2) && hasTag(tags2, 'tunnel')) { return true; }
57522
57523                 // don't flag crossing waterways and pier/highways
57524                 if (featureType1 === 'waterway' && featureType2 === 'highway' && tags2.man_made === 'pier') { return true; }
57525                 if (featureType2 === 'waterway' && featureType1 === 'highway' && tags1.man_made === 'pier') { return true; }
57526
57527                 if (featureType1 === 'building' || featureType2 === 'building') {
57528                     // for building crossings, different layers are enough
57529                     if (layer1 !== layer2) { return true; }
57530                 }
57531                 return false;
57532             }
57533
57534
57535             // highway values for which we shouldn't recommend connecting to waterways
57536             var highwaysDisallowingFords = {
57537                 motorway: true, motorway_link: true, trunk: true, trunk_link: true,
57538                 primary: true, primary_link: true, secondary: true, secondary_link: true
57539             };
57540             var nonCrossingHighways = { track: true };
57541
57542             function tagsForConnectionNodeIfAllowed(entity1, entity2, graph) {
57543                 var featureType1 = getFeatureType(entity1, graph);
57544                 var featureType2 = getFeatureType(entity2, graph);
57545
57546                 var geometry1 = entity1.geometry(graph);
57547                 var geometry2 = entity2.geometry(graph);
57548                 var bothLines = geometry1 === 'line' && geometry2 === 'line';
57549
57550                 if (featureType1 === featureType2) {
57551                     if (featureType1 === 'highway') {
57552                         var entity1IsPath = osmPathHighwayTagValues[entity1.tags.highway];
57553                         var entity2IsPath = osmPathHighwayTagValues[entity2.tags.highway];
57554                         if ((entity1IsPath || entity2IsPath) && entity1IsPath !== entity2IsPath) {
57555                             // one feature is a path but not both
57556
57557                             var roadFeature = entity1IsPath ? entity2 : entity1;
57558                             if (nonCrossingHighways[roadFeature.tags.highway]) {
57559                                 // don't mark path connections with certain roads as crossings
57560                                 return {};
57561                             }
57562                             var pathFeature = entity1IsPath ? entity1 : entity2;
57563                             if (['marked', 'unmarked'].indexOf(pathFeature.tags.crossing) !== -1) {
57564                                 // if the path is a crossing, match the crossing type
57565                                 return bothLines ? { highway: 'crossing', crossing: pathFeature.tags.crossing } : {};
57566                             }
57567                             // don't add a `crossing` subtag to ambiguous crossings
57568                             return bothLines ? { highway: 'crossing' } : {};
57569                         }
57570                         return {};
57571                     }
57572                     if (featureType1 === 'waterway') { return {}; }
57573                     if (featureType1 === 'railway') { return {}; }
57574
57575                 } else {
57576                     var featureTypes = [featureType1, featureType2];
57577                     if (featureTypes.indexOf('highway') !== -1) {
57578                         if (featureTypes.indexOf('railway') !== -1) {
57579                             if (osmPathHighwayTagValues[entity1.tags.highway] ||
57580                                 osmPathHighwayTagValues[entity2.tags.highway]) {
57581                                 // path-rail connections use this tag
57582                                 return bothLines ? { railway: 'crossing' } : {};
57583                             } else {
57584                                 // road-rail connections use this tag
57585                                 return bothLines ? { railway: 'level_crossing' } : {};
57586                             }
57587                         }
57588
57589                         if (featureTypes.indexOf('waterway') !== -1) {
57590                             // do not allow fords on structures
57591                             if (hasTag(entity1.tags, 'tunnel') && hasTag(entity2.tags, 'tunnel')) { return null; }
57592                             if (hasTag(entity1.tags, 'bridge') && hasTag(entity2.tags, 'bridge')) { return null; }
57593
57594                             if (highwaysDisallowingFords[entity1.tags.highway] ||
57595                                 highwaysDisallowingFords[entity2.tags.highway]) {
57596                                 // do not allow fords on major highways
57597                                 return null;
57598                             }
57599                             return bothLines ? { ford: 'yes' } : {};
57600                         }
57601                     }
57602                 }
57603                 return null;
57604             }
57605
57606
57607             function findCrossingsByWay(way1, graph, tree) {
57608                 var edgeCrossInfos = [];
57609                 if (way1.type !== 'way') { return edgeCrossInfos; }
57610
57611                 var way1FeatureType = getFeatureTypeForCrossingCheck(way1, graph);
57612                 if (way1FeatureType === null) { return edgeCrossInfos; }
57613
57614                 var checkedSingleCrossingWays = {};
57615
57616                 // declare vars ahead of time to reduce garbage collection
57617                 var i, j;
57618                 var extent;
57619                 var n1, n2, nA, nB, nAId, nBId;
57620                 var segment1, segment2;
57621                 var oneOnly;
57622                 var segmentInfos, segment2Info, way2, way2FeatureType;
57623                 var way1Nodes = graph.childNodes(way1);
57624                 var comparedWays = {};
57625                 for (i = 0; i < way1Nodes.length - 1; i++) {
57626                     n1 = way1Nodes[i];
57627                     n2 = way1Nodes[i + 1];
57628                     extent = geoExtent([
57629                         [
57630                             Math.min(n1.loc[0], n2.loc[0]),
57631                             Math.min(n1.loc[1], n2.loc[1])
57632                         ],
57633                         [
57634                             Math.max(n1.loc[0], n2.loc[0]),
57635                             Math.max(n1.loc[1], n2.loc[1])
57636                         ]
57637                     ]);
57638
57639                     // Optimize by only checking overlapping segments, not every segment
57640                     // of overlapping ways
57641                     segmentInfos = tree.waySegments(extent, graph);
57642
57643                     for (j = 0; j < segmentInfos.length; j++) {
57644                         segment2Info = segmentInfos[j];
57645
57646                         // don't check for self-intersection in this validation
57647                         if (segment2Info.wayId === way1.id) { continue; }
57648
57649                         // skip if this way was already checked and only one issue is needed
57650                         if (checkedSingleCrossingWays[segment2Info.wayId]) { continue; }
57651
57652                         // mark this way as checked even if there are no crossings
57653                         comparedWays[segment2Info.wayId] = true;
57654
57655                         way2 = graph.hasEntity(segment2Info.wayId);
57656                         if (!way2) { continue; }
57657
57658                         // only check crossing highway, waterway, building, and railway
57659                         way2FeatureType = getFeatureTypeForCrossingCheck(way2, graph);
57660                         if (way2FeatureType === null ||
57661                             isLegitCrossing(way1, way1FeatureType, way2, way2FeatureType)) {
57662                             continue;
57663                         }
57664
57665                         // create only one issue for building crossings
57666                         oneOnly = way1FeatureType === 'building' || way2FeatureType === 'building';
57667
57668                         nAId = segment2Info.nodes[0];
57669                         nBId = segment2Info.nodes[1];
57670                         if (nAId === n1.id || nAId === n2.id ||
57671                             nBId === n1.id || nBId === n2.id) {
57672                             // n1 or n2 is a connection node; skip
57673                             continue;
57674                         }
57675                         nA = graph.hasEntity(nAId);
57676                         if (!nA) { continue; }
57677                         nB = graph.hasEntity(nBId);
57678                         if (!nB) { continue; }
57679
57680                         segment1 = [n1.loc, n2.loc];
57681                         segment2 = [nA.loc, nB.loc];
57682                         var point = geoLineIntersection(segment1, segment2);
57683                         if (point) {
57684                             edgeCrossInfos.push({
57685                                 wayInfos: [
57686                                     {
57687                                         way: way1,
57688                                         featureType: way1FeatureType,
57689                                         edge: [n1.id, n2.id]
57690                                     },
57691                                     {
57692                                         way: way2,
57693                                         featureType: way2FeatureType,
57694                                         edge: [nA.id, nB.id]
57695                                     }
57696                                 ],
57697                                 crossPoint: point
57698                             });
57699                             if (oneOnly) {
57700                                 checkedSingleCrossingWays[way2.id] = true;
57701                                 break;
57702                             }
57703                         }
57704                     }
57705                 }
57706                 return edgeCrossInfos;
57707             }
57708
57709
57710             function waysToCheck(entity, graph) {
57711                 var featureType = getFeatureType(entity, graph);
57712                 if (!featureType) { return []; }
57713
57714                 if (entity.type === 'way') {
57715                     return [entity];
57716                 } else if (entity.type === 'relation') {
57717                     return entity.members.reduce(function(array, member) {
57718                         if (member.type === 'way' &&
57719                             // only look at geometry ways
57720                             (!member.role || member.role === 'outer' || member.role === 'inner')) {
57721                             var entity = graph.hasEntity(member.id);
57722                             // don't add duplicates
57723                             if (entity && array.indexOf(entity) === -1) {
57724                                 array.push(entity);
57725                             }
57726                         }
57727                         return array;
57728                     }, []);
57729                 }
57730                 return [];
57731             }
57732
57733
57734             var validation = function checkCrossingWays(entity, graph) {
57735
57736                 var tree = context.history().tree();
57737
57738                 var ways = waysToCheck(entity, graph);
57739
57740                 var issues = [];
57741                 // declare these here to reduce garbage collection
57742                 var wayIndex, crossingIndex, crossings;
57743                 for (wayIndex in ways) {
57744                     crossings = findCrossingsByWay(ways[wayIndex], graph, tree);
57745                     for (crossingIndex in crossings) {
57746                         issues.push(createIssue(crossings[crossingIndex], graph));
57747                     }
57748                 }
57749                 return issues;
57750             };
57751
57752
57753             function createIssue(crossing, graph) {
57754
57755                 // use the entities with the tags that define the feature type
57756                 crossing.wayInfos.sort(function(way1Info, way2Info) {
57757                     var type1 = way1Info.featureType;
57758                     var type2 = way2Info.featureType;
57759                     if (type1 === type2) {
57760                         return utilDisplayLabel(way1Info.way, graph) > utilDisplayLabel(way2Info.way, graph);
57761                     } else if (type1 === 'waterway') {
57762                         return true;
57763                     } else if (type2 === 'waterway') {
57764                         return false;
57765                     }
57766                     return type1 < type2;
57767                 });
57768                 var entities = crossing.wayInfos.map(function(wayInfo) {
57769                     return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
57770                 });
57771                 var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
57772                 var featureTypes = [crossing.wayInfos[0].featureType, crossing.wayInfos[1].featureType];
57773
57774                 var connectionTags = tagsForConnectionNodeIfAllowed(entities[0], entities[1], graph);
57775
57776                 var featureType1 = crossing.wayInfos[0].featureType;
57777                 var featureType2 = crossing.wayInfos[1].featureType;
57778
57779                 var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
57780                 var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') &&
57781                                         allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
57782                 var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') &&
57783                                         allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
57784
57785                 var subtype = [featureType1, featureType2].sort().join('-');
57786
57787                 var crossingTypeID = subtype;
57788
57789                 if (isCrossingIndoors) {
57790                     crossingTypeID = 'indoor-indoor';
57791                 } else if (isCrossingTunnels) {
57792                     crossingTypeID = 'tunnel-tunnel';
57793                 } else if (isCrossingBridges) {
57794                     crossingTypeID = 'bridge-bridge';
57795                 }
57796                 if (connectionTags && (isCrossingIndoors || isCrossingTunnels || isCrossingBridges)) {
57797                     crossingTypeID += '_connectable';
57798                 }
57799
57800                 return new validationIssue({
57801                     type: type,
57802                     subtype: subtype,
57803                     severity: 'warning',
57804                     message: function(context) {
57805                         var graph = context.graph();
57806                         var entity1 = graph.hasEntity(this.entityIds[0]),
57807                             entity2 = graph.hasEntity(this.entityIds[1]);
57808                         return (entity1 && entity2) ? _t('issues.crossing_ways.message', {
57809                             feature: utilDisplayLabel(entity1, graph),
57810                             feature2: utilDisplayLabel(entity2, graph)
57811                         }) : '';
57812                     },
57813                     reference: showReference,
57814                     entityIds: entities.map(function(entity) {
57815                         return entity.id;
57816                     }),
57817                     data: {
57818                         edges: edges,
57819                         featureTypes: featureTypes,
57820                         connectionTags: connectionTags
57821                     },
57822                     // differentiate based on the loc since two ways can cross multiple times
57823                     hash: crossing.crossPoint.toString() +
57824                         // if the edges change then so does the fix
57825                         edges.slice().sort(function(edge1, edge2) {
57826                             // order to assure hash is deterministic
57827                             return edge1[0] < edge2[0] ? -1 : 1;
57828                         }).toString() +
57829                         // ensure the correct connection tags are added in the fix
57830                         JSON.stringify(connectionTags),
57831                     loc: crossing.crossPoint,
57832                     dynamicFixes: function(context) {
57833                         var mode = context.mode();
57834                         if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) { return []; }
57835
57836                         var selectedIndex = this.entityIds[0] === mode.selectedIDs()[0] ? 0 : 1;
57837                         var selectedFeatureType = this.data.featureTypes[selectedIndex];
57838                         var otherFeatureType = this.data.featureTypes[selectedIndex === 0 ? 1 : 0];
57839
57840                         var fixes = [];
57841
57842                         if (connectionTags) {
57843                             fixes.push(makeConnectWaysFix(this.data.connectionTags));
57844                         }
57845
57846                         if (isCrossingIndoors) {
57847                             fixes.push(new validationIssueFix({
57848                                 icon: 'iD-icon-layers',
57849                                 title: _t('issues.fix.use_different_levels.title')
57850                             }));
57851                         } else if (isCrossingTunnels ||
57852                             isCrossingBridges ||
57853                             featureType1 === 'building' ||
57854                             featureType2 === 'building')  {
57855
57856                             fixes.push(makeChangeLayerFix('higher'));
57857                             fixes.push(makeChangeLayerFix('lower'));
57858
57859                         // can only add bridge/tunnel if both features are lines
57860                         } else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
57861                             context.graph().geometry(this.entityIds[1]) === 'line') {
57862
57863                             // don't recommend adding bridges to waterways since they're uncommmon
57864                             if (allowsBridge(selectedFeatureType) && selectedFeatureType !== 'waterway') {
57865                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_bridge', 'temaki-bridge', 'bridge'));
57866                             }
57867
57868                             // don't recommend adding tunnels under waterways since they're uncommmon
57869                             var skipTunnelFix = otherFeatureType === 'waterway' && selectedFeatureType !== 'waterway';
57870                             if (allowsTunnel(selectedFeatureType) && !skipTunnelFix) {
57871                                 fixes.push(makeAddBridgeOrTunnelFix('add_a_tunnel', 'temaki-tunnel', 'tunnel'));
57872                             }
57873                         }
57874
57875                         // repositioning the features is always an option
57876                         fixes.push(new validationIssueFix({
57877                             icon: 'iD-operation-move',
57878                             title: _t('issues.fix.reposition_features.title')
57879                         }));
57880
57881                         return fixes;
57882                     }
57883                 });
57884
57885                 function showReference(selection) {
57886                     selection.selectAll('.issue-reference')
57887                         .data([0])
57888                         .enter()
57889                         .append('div')
57890                         .attr('class', 'issue-reference')
57891                         .text(_t('issues.crossing_ways.' + crossingTypeID + '.reference'));
57892                 }
57893             }
57894
57895             function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){
57896                 return new validationIssueFix({
57897                     icon: iconName,
57898                     title: _t('issues.fix.' + fixTitleID + '.title'),
57899                     onClick: function(context) {
57900                         var mode = context.mode();
57901                         if (!mode || mode.id !== 'select') { return; }
57902
57903                         var selectedIDs = mode.selectedIDs();
57904                         if (selectedIDs.length !== 1) { return; }
57905
57906                         var selectedWayID = selectedIDs[0];
57907                         if (!context.hasEntity(selectedWayID)) { return; }
57908
57909                         var resultWayIDs = [selectedWayID];
57910
57911                         var edge, crossedEdge, crossedWayID;
57912                         if (this.issue.entityIds[0] === selectedWayID) {
57913                             edge = this.issue.data.edges[0];
57914                             crossedEdge = this.issue.data.edges[1];
57915                             crossedWayID = this.issue.entityIds[1];
57916                         } else {
57917                             edge = this.issue.data.edges[1];
57918                             crossedEdge = this.issue.data.edges[0];
57919                             crossedWayID = this.issue.entityIds[0];
57920                         }
57921
57922                         var crossingLoc = this.issue.loc;
57923
57924                         var projection = context.projection;
57925
57926                         var action = function actionAddStructure(graph) {
57927
57928                             var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
57929
57930                             var crossedWay = graph.hasEntity(crossedWayID);
57931                             // use the explicit width of the crossed feature as the structure length, if available
57932                             var structLengthMeters = crossedWay && crossedWay.tags.width && parseFloat(crossedWay.tags.width);
57933                             if (!structLengthMeters) {
57934                                 // if no explicit width is set, approximate the width based on the tags
57935                                 structLengthMeters = crossedWay && crossedWay.impliedLineWidthMeters();
57936                             }
57937                             if (structLengthMeters) {
57938                                 if (getFeatureType(crossedWay, graph) === 'railway') {
57939                                     // bridges over railways are generally much longer than the rail bed itself, compensate
57940                                     structLengthMeters *= 2;
57941                                 }
57942                             } else {
57943                                 // should ideally never land here since all rail/water/road tags should have an implied width
57944                                 structLengthMeters = 8;
57945                             }
57946
57947                             var a1 = geoAngle(edgeNodes[0], edgeNodes[1], projection) + Math.PI;
57948                             var a2 = geoAngle(graph.entity(crossedEdge[0]), graph.entity(crossedEdge[1]), projection) + Math.PI;
57949                             var crossingAngle = Math.max(a1, a2) - Math.min(a1, a2);
57950                             if (crossingAngle > Math.PI) { crossingAngle -= Math.PI; }
57951                             // lengthen the structure to account for the angle of the crossing
57952                             structLengthMeters = ((structLengthMeters / 2) / Math.sin(crossingAngle)) * 2;
57953
57954                             // add padding since the structure must extend past the edges of the crossed feature
57955                             structLengthMeters += 4;
57956
57957                             // clamp the length to a reasonable range
57958                             structLengthMeters = Math.min(Math.max(structLengthMeters, 4), 50);
57959
57960                             function geomToProj(geoPoint) {
57961                                 return [
57962                                     geoLonToMeters(geoPoint[0], geoPoint[1]),
57963                                     geoLatToMeters(geoPoint[1])
57964                                 ];
57965                             }
57966                             function projToGeom(projPoint) {
57967                                 var lat = geoMetersToLat(projPoint[1]);
57968                                 return [
57969                                     geoMetersToLon(projPoint[0], lat),
57970                                     lat
57971                                 ];
57972                             }
57973
57974                             var projEdgeNode1 = geomToProj(edgeNodes[0].loc);
57975                             var projEdgeNode2 = geomToProj(edgeNodes[1].loc);
57976
57977                             var projectedAngle = geoVecAngle(projEdgeNode1, projEdgeNode2);
57978
57979                             var projectedCrossingLoc = geomToProj(crossingLoc);
57980                             var linearToSphericalMetersRatio = geoVecLength(projEdgeNode1, projEdgeNode2) /
57981                                 geoSphericalDistance(edgeNodes[0].loc, edgeNodes[1].loc);
57982
57983                             function locSphericalDistanceFromCrossingLoc(angle, distanceMeters) {
57984                                 var lengthSphericalMeters = distanceMeters * linearToSphericalMetersRatio;
57985                                 return projToGeom([
57986                                     projectedCrossingLoc[0] + Math.cos(angle) * lengthSphericalMeters,
57987                                     projectedCrossingLoc[1] + Math.sin(angle) * lengthSphericalMeters
57988                                 ]);
57989                             }
57990
57991                             var endpointLocGetter1 = function(lengthMeters) {
57992                                 return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
57993                             };
57994                             var endpointLocGetter2 = function(lengthMeters) {
57995                                 return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
57996                             };
57997
57998                             // avoid creating very short edges from splitting too close to another node
57999                             var minEdgeLengthMeters = 0.55;
58000
58001                             // decide where to bound the structure along the way, splitting as necessary
58002                             function determineEndpoint(edge, endNode, locGetter) {
58003                                 var newNode;
58004
58005                                 var idealLengthMeters = structLengthMeters / 2;
58006
58007                                 // distance between the crossing location and the end of the edge,
58008                                 // the maximum length of this side of the structure
58009                                 var crossingToEdgeEndDistance = geoSphericalDistance(crossingLoc, endNode.loc);
58010
58011                                 if (crossingToEdgeEndDistance - idealLengthMeters > minEdgeLengthMeters) {
58012                                     // the edge is long enough to insert a new node
58013
58014                                     // the loc that would result in the full expected length
58015                                     var idealNodeLoc = locGetter(idealLengthMeters);
58016
58017                                     newNode = osmNode();
58018                                     graph = actionAddMidpoint({ loc: idealNodeLoc, edge: edge }, newNode)(graph);
58019
58020                                 } else {
58021                                     var edgeCount = 0;
58022                                     endNode.parentIntersectionWays(graph).forEach(function(way) {
58023                                         way.nodes.forEach(function(nodeID) {
58024                                             if (nodeID === endNode.id) {
58025                                                 if ((endNode.id === way.first() && endNode.id !== way.last()) ||
58026                                                     (endNode.id === way.last() && endNode.id !== way.first())) {
58027                                                     edgeCount += 1;
58028                                                 } else {
58029                                                     edgeCount += 2;
58030                                                 }
58031                                             }
58032                                         });
58033                                     });
58034
58035                                     if (edgeCount >= 3) {
58036                                         // the end node is a junction, try to leave a segment
58037                                         // between it and the structure - #7202
58038
58039                                         var insetLength = crossingToEdgeEndDistance - minEdgeLengthMeters;
58040                                         if (insetLength > minEdgeLengthMeters) {
58041                                             var insetNodeLoc = locGetter(insetLength);
58042                                             newNode = osmNode();
58043                                             graph = actionAddMidpoint({ loc: insetNodeLoc, edge: edge }, newNode)(graph);
58044                                         }
58045                                     }
58046                                 }
58047
58048                                 // if the edge is too short to subdivide as desired, then
58049                                 // just bound the structure at the existing end node
58050                                 if (!newNode) { newNode = endNode; }
58051
58052                                 var splitAction = actionSplit(newNode.id)
58053                                     .limitWays(resultWayIDs); // only split selected or created ways
58054
58055                                 // do the split
58056                                 graph = splitAction(graph);
58057                                 if (splitAction.getCreatedWayIDs().length) {
58058                                     resultWayIDs.push(splitAction.getCreatedWayIDs()[0]);
58059                                 }
58060
58061                                 return newNode;
58062                             }
58063
58064                             var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
58065                             var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
58066
58067                             var structureWay = resultWayIDs.map(function(id) {
58068                                 return graph.entity(id);
58069                             }).find(function(way) {
58070                                 return way.nodes.indexOf(structEndNode1.id) !== -1 &&
58071                                     way.nodes.indexOf(structEndNode2.id) !== -1;
58072                             });
58073
58074                             var tags = Object.assign({}, structureWay.tags); // copy tags
58075                             if (bridgeOrTunnel === 'bridge'){
58076                                 tags.bridge = 'yes';
58077                                 tags.layer = '1';
58078                             } else {
58079                                 var tunnelValue = 'yes';
58080                                 if (getFeatureType(structureWay, graph) === 'waterway') {
58081                                     // use `tunnel=culvert` for waterways by default
58082                                     tunnelValue = 'culvert';
58083                                 }
58084                                 tags.tunnel = tunnelValue;
58085                                 tags.layer = '-1';
58086                             }
58087                             // apply the structure tags to the way
58088                             graph = actionChangeTags(structureWay.id, tags)(graph);
58089                             return graph;
58090                         };
58091
58092                         context.perform(action, _t('issues.fix.' + fixTitleID + '.annotation'));
58093                         context.enter(modeSelect(context, resultWayIDs));
58094                     }
58095                 });
58096             }
58097
58098             function makeConnectWaysFix(connectionTags) {
58099
58100                 var fixTitleID = 'connect_features';
58101                 if (connectionTags.ford) {
58102                     fixTitleID = 'connect_using_ford';
58103                 }
58104
58105                 return new validationIssueFix({
58106                     icon: 'iD-icon-crossing',
58107                     title: _t('issues.fix.' + fixTitleID + '.title'),
58108                     onClick: function(context) {
58109                         var loc = this.issue.loc;
58110                         var connectionTags = this.issue.data.connectionTags;
58111                         var edges = this.issue.data.edges;
58112
58113                         context.perform(
58114                             function actionConnectCrossingWays(graph) {
58115                                 // create the new node for the points
58116                                 var node = osmNode({ loc: loc, tags: connectionTags });
58117                                 graph = graph.replace(node);
58118
58119                                 var nodesToMerge = [node.id];
58120                                 var mergeThresholdInMeters = 0.75;
58121
58122                                 edges.forEach(function(edge) {
58123                                     var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
58124                                     var closestNodeInfo = geoSphericalClosestNode(edgeNodes, loc);
58125                                     // if there is already a point nearby, use that
58126                                     if (closestNodeInfo.distance < mergeThresholdInMeters) {
58127                                         nodesToMerge.push(closestNodeInfo.node.id);
58128                                     // else add the new node to the way
58129                                     } else {
58130                                         graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph);
58131                                     }
58132                                 });
58133
58134                                 if (nodesToMerge.length > 1) {
58135                                     // if we're using nearby nodes, merge them with the new node
58136                                     graph = actionMergeNodes(nodesToMerge, loc)(graph);
58137                                 }
58138
58139                                 return graph;
58140                             },
58141                             _t('issues.fix.connect_crossing_features.annotation')
58142                         );
58143                     }
58144                 });
58145             }
58146
58147             function makeChangeLayerFix(higherOrLower) {
58148                 return new validationIssueFix({
58149                     icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
58150                     title: _t('issues.fix.tag_this_as_' + higherOrLower + '.title'),
58151                     onClick: function(context) {
58152
58153                         var mode = context.mode();
58154                         if (!mode || mode.id !== 'select') { return; }
58155
58156                         var selectedIDs = mode.selectedIDs();
58157                         if (selectedIDs.length !== 1) { return; }
58158
58159                         var selectedID = selectedIDs[0];
58160                         if (!this.issue.entityIds.some(function(entityId) {
58161                             return entityId === selectedID;
58162                         })) { return; }
58163
58164                         var entity = context.hasEntity(selectedID);
58165                         if (!entity) { return; }
58166
58167                         var tags = Object.assign({}, entity.tags);   // shallow copy
58168                         var layer = tags.layer && Number(tags.layer);
58169                         if (layer && !isNaN(layer)) {
58170                             if (higherOrLower === 'higher') {
58171                                 layer += 1;
58172                             } else {
58173                                 layer -= 1;
58174                             }
58175                         } else {
58176                             if (higherOrLower === 'higher') {
58177                                 layer = 1;
58178                             } else {
58179                                 layer = -1;
58180                             }
58181                         }
58182                         tags.layer = layer.toString();
58183                         context.perform(
58184                             actionChangeTags(entity.id, tags),
58185                             _t('operations.change_tags.annotation')
58186                         );
58187                     }
58188                 });
58189             }
58190
58191             validation.type = type;
58192
58193             return validation;
58194         }
58195
58196         function validationDisconnectedWay() {
58197             var type = 'disconnected_way';
58198
58199             function isTaggedAsHighway(entity) {
58200                 return osmRoutableHighwayTagValues[entity.tags.highway];
58201             }
58202
58203             var validation = function checkDisconnectedWay(entity, graph) {
58204
58205                 var routingIslandWays = routingIslandForEntity(entity);
58206                 if (!routingIslandWays) { return []; }
58207
58208                 return [new validationIssue({
58209                     type: type,
58210                     subtype: 'highway',
58211                     severity: 'warning',
58212                     message: function(context) {
58213                         if (this.entityIds.length === 1) {
58214                             var entity = context.hasEntity(this.entityIds[0]);
58215                             return entity ? _t('issues.disconnected_way.highway.message', { highway: utilDisplayLabel(entity, context.graph()) }) : '';
58216                         }
58217                         return _t('issues.disconnected_way.routable.message.multiple', { count: this.entityIds.length.toString() });
58218                     },
58219                     reference: showReference,
58220                     entityIds: Array.from(routingIslandWays).map(function(way) { return way.id; }),
58221                     dynamicFixes: makeFixes
58222                 })];
58223
58224
58225                 function makeFixes(context) {
58226
58227                     var fixes = [];
58228
58229                     var singleEntity = this.entityIds.length === 1 && context.hasEntity(this.entityIds[0]);
58230
58231                     if (singleEntity) {
58232
58233                         if (singleEntity.type === 'way' && !singleEntity.isClosed()) {
58234
58235                             var textDirection = _mainLocalizer.textDirection();
58236
58237                             var startFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.first(), 'start');
58238                             if (startFix) { fixes.push(startFix); }
58239
58240                             var endFix = makeContinueDrawingFixIfAllowed(textDirection, singleEntity.last(), 'end');
58241                             if (endFix) { fixes.push(endFix); }
58242                         }
58243                         if (!fixes.length) {
58244                             fixes.push(new validationIssueFix({
58245                                 title: _t('issues.fix.connect_feature.title')
58246                             }));
58247                         }
58248
58249                         fixes.push(new validationIssueFix({
58250                             icon: 'iD-operation-delete',
58251                             title: _t('issues.fix.delete_feature.title'),
58252                             entityIds: [singleEntity.id],
58253                             onClick: function(context) {
58254                                 var id = this.issue.entityIds[0];
58255                                 var operation = operationDelete(context, [id]);
58256                                 if (!operation.disabled()) {
58257                                     operation();
58258                                 }
58259                             }
58260                         }));
58261                     } else {
58262                         fixes.push(new validationIssueFix({
58263                             title: _t('issues.fix.connect_features.title')
58264                         }));
58265                     }
58266
58267                     return fixes;
58268                 }
58269
58270
58271                 function showReference(selection) {
58272                     selection.selectAll('.issue-reference')
58273                         .data([0])
58274                         .enter()
58275                         .append('div')
58276                         .attr('class', 'issue-reference')
58277                         .text(_t('issues.disconnected_way.routable.reference'));
58278                 }
58279
58280                 function routingIslandForEntity(entity) {
58281
58282                     var routingIsland = new Set();  // the interconnected routable features
58283                     var waysToCheck = [];           // the queue of remaining routable ways to traverse
58284
58285                     function queueParentWays(node) {
58286                         graph.parentWays(node).forEach(function(parentWay) {
58287                             if (!routingIsland.has(parentWay) &&    // only check each feature once
58288                                 isRoutableWay(parentWay, false)) {  // only check routable features
58289                                 routingIsland.add(parentWay);
58290                                 waysToCheck.push(parentWay);
58291                             }
58292                         });
58293                     }
58294
58295                     if (entity.type === 'way' && isRoutableWay(entity, true)) {
58296
58297                         routingIsland.add(entity);
58298                         waysToCheck.push(entity);
58299
58300                     } else if (entity.type === 'node' && isRoutableNode(entity)) {
58301
58302                         routingIsland.add(entity);
58303                         queueParentWays(entity);
58304
58305                     } else {
58306                         // this feature isn't routable, cannot be a routing island
58307                         return null;
58308                     }
58309
58310                     while (waysToCheck.length) {
58311                         var wayToCheck = waysToCheck.pop();
58312                         var childNodes = graph.childNodes(wayToCheck);
58313                         for (var i in childNodes) {
58314                             var vertex = childNodes[i];
58315
58316                             if (isConnectedVertex(vertex)) {
58317                                 // found a link to the wider network, not a routing island
58318                                 return null;
58319                             }
58320
58321                             if (isRoutableNode(vertex)) {
58322                                 routingIsland.add(vertex);
58323                             }
58324
58325                             queueParentWays(vertex);
58326                         }
58327                     }
58328
58329                     // no network link found, this is a routing island, return its members
58330                     return routingIsland;
58331                 }
58332
58333                 function isConnectedVertex(vertex) {
58334                     // assume ways overlapping unloaded tiles are connected to the wider road network  - #5938
58335                     var osm = services.osm;
58336                     if (osm && !osm.isDataLoaded(vertex.loc)) { return true; }
58337
58338                     // entrances are considered connected
58339                     if (vertex.tags.entrance &&
58340                         vertex.tags.entrance !== 'no') { return true; }
58341                     if (vertex.tags.amenity === 'parking_entrance') { return true; }
58342
58343                     return false;
58344                 }
58345
58346                 function isRoutableNode(node) {
58347                     // treat elevators as distinct features in the highway network
58348                     if (node.tags.highway === 'elevator') { return true; }
58349                     return false;
58350                 }
58351
58352                 function isRoutableWay(way, ignoreInnerWays) {
58353                     if (isTaggedAsHighway(way) || way.tags.route === 'ferry') { return true; }
58354
58355                     return graph.parentRelations(way).some(function(parentRelation) {
58356                         if (parentRelation.tags.type === 'route' &&
58357                             parentRelation.tags.route === 'ferry') { return true; }
58358
58359                         if (parentRelation.isMultipolygon() &&
58360                             isTaggedAsHighway(parentRelation) &&
58361                             (!ignoreInnerWays || parentRelation.memberById(way.id).role !== 'inner')) { return true; }
58362                     });
58363                 }
58364
58365                 function makeContinueDrawingFixIfAllowed(textDirection, vertexID, whichEnd) {
58366                     var vertex = graph.hasEntity(vertexID);
58367                     if (!vertex || vertex.tags.noexit === 'yes') { return null; }
58368
58369                     var useLeftContinue = (whichEnd === 'start' && textDirection === 'ltr') ||
58370                         (whichEnd === 'end' && textDirection === 'rtl');
58371
58372                     return new validationIssueFix({
58373                         icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58374                         title: _t('issues.fix.continue_from_' + whichEnd + '.title'),
58375                         entityIds: [vertexID],
58376                         onClick: function(context) {
58377                             var wayId = this.issue.entityIds[0];
58378                             var way = context.hasEntity(wayId);
58379                             var vertexId = this.entityIds[0];
58380                             var vertex = context.hasEntity(vertexId);
58381
58382                             if (!way || !vertex) { return; }
58383
58384                             // make sure the vertex is actually visible and editable
58385                             var map = context.map();
58386                             if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58387                                 map.zoomToEase(vertex);
58388                             }
58389
58390                             context.enter(
58391                                 modeDrawLine(context, wayId, context.graph(), 'line', way.affix(vertexId), true)
58392                             );
58393                         }
58394                     });
58395                 }
58396
58397             };
58398
58399             validation.type = type;
58400
58401             return validation;
58402         }
58403
58404         function validationFormatting() {
58405             var type = 'invalid_format';
58406
58407             var validation = function(entity) {
58408                 var issues = [];
58409
58410                 function isValidEmail(email) {
58411                     // Emails in OSM are going to be official so they should be pretty simple
58412                     // Using negated lists to better support all possible unicode characters (#6494)
58413                     var valid_email = /^[^\(\)\\,":;<>@\[\]]+@[^\(\)\\,":;<>@\[\]\.]+(?:\.[a-z0-9-]+)*$/i;
58414
58415                     // An empty value is also acceptable
58416                     return (!email || valid_email.test(email));
58417                 }
58418                 /*
58419                 function isSchemePresent(url) {
58420                     var valid_scheme = /^https?:\/\//i;
58421                     return (!url || valid_scheme.test(url));
58422                 }
58423                 */
58424                 function showReferenceEmail(selection) {
58425                     selection.selectAll('.issue-reference')
58426                         .data([0])
58427                         .enter()
58428                         .append('div')
58429                         .attr('class', 'issue-reference')
58430                         .text(_t('issues.invalid_format.email.reference'));
58431                 }
58432                 /*
58433                 function showReferenceWebsite(selection) {
58434                     selection.selectAll('.issue-reference')
58435                         .data([0])
58436                         .enter()
58437                         .append('div')
58438                         .attr('class', 'issue-reference')
58439                         .text(t('issues.invalid_format.website.reference'));
58440                 }
58441
58442                 if (entity.tags.website) {
58443                     // Multiple websites are possible
58444                     // If ever we support ES6, arrow functions make this nicer
58445                     var websites = entity.tags.website
58446                         .split(';')
58447                         .map(function(s) { return s.trim(); })
58448                         .filter(function(x) { return !isSchemePresent(x); });
58449
58450                     if (websites.length) {
58451                         issues.push(new validationIssue({
58452                             type: type,
58453                             subtype: 'website',
58454                             severity: 'warning',
58455                             message: function(context) {
58456                                 var entity = context.hasEntity(this.entityIds[0]);
58457                                 return entity ? t('issues.invalid_format.website.message' + this.data,
58458                                     { feature: utilDisplayLabel(entity, context.graph()), site: websites.join(', ') }) : '';
58459                             },
58460                             reference: showReferenceWebsite,
58461                             entityIds: [entity.id],
58462                             hash: websites.join(),
58463                             data: (websites.length > 1) ? '_multi' : ''
58464                         }));
58465                     }
58466                 }
58467                 */
58468                 if (entity.tags.email) {
58469                     // Multiple emails are possible
58470                     var emails = entity.tags.email
58471                         .split(';')
58472                         .map(function(s) { return s.trim(); })
58473                         .filter(function(x) { return !isValidEmail(x); });
58474
58475                     if (emails.length) {
58476                         issues.push(new validationIssue({
58477                             type: type,
58478                             subtype: 'email',
58479                             severity: 'warning',
58480                             message: function(context) {
58481                                 var entity = context.hasEntity(this.entityIds[0]);
58482                                 return entity ? _t('issues.invalid_format.email.message' + this.data,
58483                                     { feature: utilDisplayLabel(entity, context.graph()), email: emails.join(', ') }) : '';
58484                             },
58485                             reference: showReferenceEmail,
58486                             entityIds: [entity.id],
58487                             hash: emails.join(),
58488                             data: (emails.length > 1) ? '_multi' : ''
58489                         }));
58490                     }
58491                 }
58492
58493                 return issues;
58494             };
58495
58496             validation.type = type;
58497
58498             return validation;
58499         }
58500
58501         function validationHelpRequest(context) {
58502             var type = 'help_request';
58503
58504             var validation = function checkFixmeTag(entity) {
58505
58506                 if (!entity.tags.fixme) { return []; }
58507
58508                 // don't flag fixmes on features added by the user
58509                 if (entity.version === undefined) { return []; }
58510
58511                 if (entity.v !== undefined) {
58512                     var baseEntity = context.history().base().hasEntity(entity.id);
58513                     // don't flag fixmes added by the user on existing features
58514                     if (!baseEntity || !baseEntity.tags.fixme) { return []; }
58515                 }
58516
58517                 return [new validationIssue({
58518                     type: type,
58519                     subtype: 'fixme_tag',
58520                     severity: 'warning',
58521                     message: function(context) {
58522                         var entity = context.hasEntity(this.entityIds[0]);
58523                         return entity ? _t('issues.fixme_tag.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
58524                     },
58525                     dynamicFixes: function() {
58526                         return [
58527                             new validationIssueFix({
58528                                 title: _t('issues.fix.address_the_concern.title')
58529                             })
58530                         ];
58531                     },
58532                     reference: showReference,
58533                     entityIds: [entity.id]
58534                 })];
58535
58536                 function showReference(selection) {
58537                     selection.selectAll('.issue-reference')
58538                         .data([0])
58539                         .enter()
58540                         .append('div')
58541                         .attr('class', 'issue-reference')
58542                         .text(_t('issues.fixme_tag.reference'));
58543                 }
58544             };
58545
58546             validation.type = type;
58547
58548             return validation;
58549         }
58550
58551         function validationImpossibleOneway() {
58552             var type = 'impossible_oneway';
58553
58554             var validation = function checkImpossibleOneway(entity, graph) {
58555
58556                 if (entity.type !== 'way' || entity.geometry(graph) !== 'line') { return []; }
58557
58558                 if (entity.isClosed()) { return []; }
58559
58560                 if (!typeForWay(entity)) { return []; }
58561
58562                 if (!isOneway(entity)) { return []; }
58563
58564                 var firstIssues = issuesForNode(entity, entity.first());
58565                 var lastIssues = issuesForNode(entity, entity.last());
58566
58567                 return firstIssues.concat(lastIssues);
58568
58569                 function typeForWay(way) {
58570                     if (way.geometry(graph) !== 'line') { return null; }
58571
58572                     if (osmRoutableHighwayTagValues[way.tags.highway]) { return 'highway'; }
58573                     if (osmFlowingWaterwayTagValues[way.tags.waterway]) { return 'waterway'; }
58574                     return null;
58575                 }
58576
58577                 function isOneway(way) {
58578                     if (way.tags.oneway === 'yes') { return true; }
58579                     if (way.tags.oneway) { return false; }
58580
58581                     for (var key in way.tags) {
58582                         if (osmOneWayTags[key] && osmOneWayTags[key][way.tags[key]]) {
58583                             return true;
58584                         }
58585                     }
58586                     return false;
58587                 }
58588
58589                 function nodeOccursMoreThanOnce(way, nodeID) {
58590                     var occurences = 0;
58591                     for (var index in way.nodes) {
58592                         if (way.nodes[index] === nodeID) {
58593                             occurences += 1;
58594                             if (occurences > 1) { return true; }
58595                         }
58596                     }
58597                     return false;
58598                 }
58599
58600                 function isConnectedViaOtherTypes(way, node) {
58601
58602                     var wayType = typeForWay(way);
58603
58604                     if (wayType === 'highway') {
58605                         // entrances are considered connected
58606                         if (node.tags.entrance && node.tags.entrance !== 'no') { return true; }
58607                         if (node.tags.amenity === 'parking_entrance') { return true; }
58608                     } else if (wayType === 'waterway') {
58609                         if (node.id === way.first()) {
58610                             // multiple waterways may start at the same spring
58611                             if (node.tags.natural === 'spring') { return true; }
58612                         } else {
58613                             // multiple waterways may end at the same drain
58614                             if (node.tags.manhole === 'drain') { return true; }
58615                         }
58616                     }
58617
58618                     return graph.parentWays(node).some(function(parentWay) {
58619                         if (parentWay.id === way.id) { return false; }
58620
58621                         if (wayType === 'highway') {
58622
58623                             // allow connections to highway areas
58624                             if (parentWay.geometry(graph) === 'area' &&
58625                                 osmRoutableHighwayTagValues[parentWay.tags.highway]) { return true; }
58626
58627                             // count connections to ferry routes as connected
58628                             if (parentWay.tags.route === 'ferry') { return true; }
58629
58630                             return graph.parentRelations(parentWay).some(function(parentRelation) {
58631                                 if (parentRelation.tags.type === 'route' &&
58632                                     parentRelation.tags.route === 'ferry') { return true; }
58633
58634                                 // allow connections to highway multipolygons
58635                                 return parentRelation.isMultipolygon() && osmRoutableHighwayTagValues[parentRelation.tags.highway];
58636                             });
58637                         } else if (wayType === 'waterway') {
58638                             // multiple waterways may start or end at a water body at the same node
58639                             if (parentWay.tags.natural === 'water' ||
58640                                 parentWay.tags.natural === 'coastline') { return true; }
58641                         }
58642                         return false;
58643                     });
58644                 }
58645
58646                 function issuesForNode(way, nodeID) {
58647
58648                     var isFirst = nodeID === way.first();
58649
58650                     var wayType = typeForWay(way);
58651
58652                     // ignore if this way is self-connected at this node
58653                     if (nodeOccursMoreThanOnce(way, nodeID)) { return []; }
58654
58655                     var osm = services.osm;
58656                     if (!osm) { return []; }
58657
58658                     var node = graph.hasEntity(nodeID);
58659
58660                     // ignore if this node or its tile are unloaded
58661                     if (!node || !osm.isDataLoaded(node.loc)) { return []; }
58662
58663                     if (isConnectedViaOtherTypes(way, node)) { return []; }
58664
58665                     var attachedWaysOfSameType = graph.parentWays(node).filter(function(parentWay) {
58666                         if (parentWay.id === way.id) { return false; }
58667                         return typeForWay(parentWay) === wayType;
58668                     });
58669
58670                     // assume it's okay for waterways to start or end disconnected for now
58671                     if (wayType === 'waterway' && attachedWaysOfSameType.length === 0) { return []; }
58672
58673                     var attachedOneways = attachedWaysOfSameType.filter(function(attachedWay) {
58674                         return isOneway(attachedWay);
58675                     });
58676
58677                     // ignore if the way is connected to some non-oneway features
58678                     if (attachedOneways.length < attachedWaysOfSameType.length) { return []; }
58679
58680                     if (attachedOneways.length) {
58681                         var connectedEndpointsOkay = attachedOneways.some(function(attachedOneway) {
58682                             if ((isFirst ? attachedOneway.first() : attachedOneway.last()) !== nodeID) { return true; }
58683                             if (nodeOccursMoreThanOnce(attachedOneway, nodeID)) { return true; }
58684                             return false;
58685                         });
58686                         if (connectedEndpointsOkay) { return []; }
58687                     }
58688
58689                     var placement = isFirst ? 'start' : 'end',
58690                         messageID = wayType + '.',
58691                         referenceID = wayType + '.';
58692
58693                     if (wayType === 'waterway') {
58694                         messageID += 'connected.' + placement;
58695                         referenceID += 'connected';
58696                     } else {
58697                         messageID += placement;
58698                         referenceID += placement;
58699                     }
58700
58701                     return [new validationIssue({
58702                         type: type,
58703                         subtype: wayType,
58704                         severity: 'warning',
58705                         message: function(context) {
58706                             var entity = context.hasEntity(this.entityIds[0]);
58707                             return entity ? _t('issues.impossible_oneway.' + messageID + '.message', {
58708                                 feature: utilDisplayLabel(entity, context.graph())
58709                             }) : '';
58710                         },
58711                         reference: getReference(referenceID),
58712                         entityIds: [way.id, node.id],
58713                         dynamicFixes: function() {
58714
58715                             var fixes = [];
58716
58717                             if (attachedOneways.length) {
58718                                 fixes.push(new validationIssueFix({
58719                                     icon: 'iD-operation-reverse',
58720                                     title: _t('issues.fix.reverse_feature.title'),
58721                                     entityIds: [way.id],
58722                                     onClick: function(context) {
58723                                         var id = this.issue.entityIds[0];
58724                                         context.perform(actionReverse(id), _t('operations.reverse.annotation'));
58725                                     }
58726                                 }));
58727                             }
58728                             if (node.tags.noexit !== 'yes') {
58729                                 var textDirection = _mainLocalizer.textDirection();
58730                                 var useLeftContinue = (isFirst && textDirection === 'ltr') ||
58731                                     (!isFirst && textDirection === 'rtl');
58732                                 fixes.push(new validationIssueFix({
58733                                     icon: 'iD-operation-continue' + (useLeftContinue ? '-left' : ''),
58734                                     title: _t('issues.fix.continue_from_' + (isFirst ? 'start' : 'end') + '.title'),
58735                                     onClick: function(context) {
58736                                         var entityID = this.issue.entityIds[0];
58737                                         var vertexID = this.issue.entityIds[1];
58738                                         var way = context.entity(entityID);
58739                                         var vertex = context.entity(vertexID);
58740                                         continueDrawing(way, vertex, context);
58741                                     }
58742                                 }));
58743                             }
58744
58745                             return fixes;
58746                         },
58747                         loc: node.loc
58748                     })];
58749
58750                     function getReference(referenceID) {
58751                         return function showReference(selection) {
58752                             selection.selectAll('.issue-reference')
58753                                 .data([0])
58754                                 .enter()
58755                                 .append('div')
58756                                 .attr('class', 'issue-reference')
58757                                 .text(_t('issues.impossible_oneway.' + referenceID + '.reference'));
58758                         };
58759                     }
58760                 }
58761             };
58762
58763             function continueDrawing(way, vertex, context) {
58764                 // make sure the vertex is actually visible and editable
58765                 var map = context.map();
58766                 if (!context.editable() || !map.trimmedExtent().contains(vertex.loc)) {
58767                     map.zoomToEase(vertex);
58768                 }
58769
58770                 context.enter(
58771                     modeDrawLine(context, way.id, context.graph(), 'line', way.affix(vertex.id), true)
58772                 );
58773             }
58774
58775             validation.type = type;
58776
58777             return validation;
58778         }
58779
58780         function validationIncompatibleSource() {
58781             var type = 'incompatible_source';
58782             var invalidSources = [
58783                 {
58784                     id:'google', regex:'google', exceptRegex: 'books.google|Google Books|drive.google|googledrive|Google Drive'
58785                 }
58786             ];
58787
58788             var validation = function checkIncompatibleSource(entity) {
58789
58790                 var entitySources = entity.tags && entity.tags.source && entity.tags.source.split(';');
58791
58792                 if (!entitySources) { return []; }
58793
58794                 var issues = [];
58795
58796                 invalidSources.forEach(function(invalidSource) {
58797
58798                     var hasInvalidSource = entitySources.some(function(source) {
58799                         if (!source.match(new RegExp(invalidSource.regex, 'i'))) { return false; }
58800                         if (invalidSource.exceptRegex && source.match(new RegExp(invalidSource.exceptRegex, 'i'))) { return false; }
58801                         return true;
58802                     });
58803
58804                     if (!hasInvalidSource) { return; }
58805
58806                     issues.push(new validationIssue({
58807                         type: type,
58808                         severity: 'warning',
58809                         message: function(context) {
58810                             var entity = context.hasEntity(this.entityIds[0]);
58811                             return entity ? _t('issues.incompatible_source.' + invalidSource.id + '.feature.message', {
58812                                 feature: utilDisplayLabel(entity, context.graph())
58813                             }) : '';
58814                         },
58815                         reference: getReference(invalidSource.id),
58816                         entityIds: [entity.id],
58817                         dynamicFixes: function() {
58818                             return [
58819                                 new validationIssueFix({
58820                                     title: _t('issues.fix.remove_proprietary_data.title')
58821                                 })
58822                             ];
58823                         }
58824                     }));
58825                 });
58826
58827                 return issues;
58828
58829
58830                 function getReference(id) {
58831                     return function showReference(selection) {
58832                         selection.selectAll('.issue-reference')
58833                             .data([0])
58834                             .enter()
58835                             .append('div')
58836                             .attr('class', 'issue-reference')
58837                             .text(_t('issues.incompatible_source.' + id + '.reference'));
58838                     };
58839                 }
58840             };
58841
58842             validation.type = type;
58843
58844             return validation;
58845         }
58846
58847         function validationMaprules() {
58848             var type = 'maprules';
58849
58850             var validation = function checkMaprules(entity, graph) {
58851                 if (!services.maprules) { return []; }
58852
58853                 var rules = services.maprules.validationRules();
58854                 var issues = [];
58855
58856                 for (var i = 0; i < rules.length; i++) {
58857                     var rule = rules[i];
58858                     rule.findIssues(entity, graph, issues);
58859                 }
58860
58861                 return issues;
58862             };
58863
58864
58865             validation.type = type;
58866
58867             return validation;
58868         }
58869
58870         function validationMismatchedGeometry() {
58871             var type = 'mismatched_geometry';
58872
58873             function tagSuggestingLineIsArea(entity) {
58874                 if (entity.type !== 'way' || entity.isClosed()) { return null; }
58875
58876                 var tagSuggestingArea = entity.tagSuggestingArea();
58877                 if (!tagSuggestingArea) {
58878                     return null;
58879                 }
58880
58881                 var asLine = _mainPresetIndex.matchTags(tagSuggestingArea, 'line');
58882                 var asArea = _mainPresetIndex.matchTags(tagSuggestingArea, 'area');
58883                 if (asLine && asArea && (asLine === asArea)) {
58884                     // these tags also allow lines and making this an area wouldn't matter
58885                     return null;
58886                 }
58887
58888                 return tagSuggestingArea;
58889             }
58890
58891
58892             function makeConnectEndpointsFixOnClick(way, graph) {
58893                 // must have at least three nodes to close this automatically
58894                 if (way.nodes.length < 3) { return null; }
58895
58896                 var nodes = graph.childNodes(way), testNodes;
58897                 var firstToLastDistanceMeters = geoSphericalDistance(nodes[0].loc, nodes[nodes.length-1].loc);
58898
58899                 // if the distance is very small, attempt to merge the endpoints
58900                 if (firstToLastDistanceMeters < 0.75) {
58901                     testNodes = nodes.slice();   // shallow copy
58902                     testNodes.pop();
58903                     testNodes.push(testNodes[0]);
58904                     // make sure this will not create a self-intersection
58905                     if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58906                         return function(context) {
58907                             var way = context.entity(this.issue.entityIds[0]);
58908                             context.perform(
58909                                 actionMergeNodes([way.nodes[0], way.nodes[way.nodes.length-1]], nodes[0].loc),
58910                                 _t('issues.fix.connect_endpoints.annotation')
58911                             );
58912                         };
58913                     }
58914                 }
58915
58916                 // if the points were not merged, attempt to close the way
58917                 testNodes = nodes.slice();   // shallow copy
58918                 testNodes.push(testNodes[0]);
58919                 // make sure this will not create a self-intersection
58920                 if (!geoHasSelfIntersections(testNodes, testNodes[0].id)) {
58921                     return function(context) {
58922                         var wayId = this.issue.entityIds[0];
58923                         var way = context.entity(wayId);
58924                         var nodeId = way.nodes[0];
58925                         var index = way.nodes.length;
58926                         context.perform(
58927                             actionAddVertex(wayId, nodeId, index),
58928                             _t('issues.fix.connect_endpoints.annotation')
58929                         );
58930                     };
58931                 }
58932             }
58933
58934             function lineTaggedAsAreaIssue(entity) {
58935
58936                 var tagSuggestingArea = tagSuggestingLineIsArea(entity);
58937                 if (!tagSuggestingArea) { return null; }
58938
58939                 return new validationIssue({
58940                     type: type,
58941                     subtype: 'area_as_line',
58942                     severity: 'warning',
58943                     message: function(context) {
58944                         var entity = context.hasEntity(this.entityIds[0]);
58945                         return entity ? _t('issues.tag_suggests_area.message', {
58946                             feature: utilDisplayLabel(entity, context.graph()),
58947                             tag: utilTagText({ tags: tagSuggestingArea })
58948                         }) : '';
58949                     },
58950                     reference: showReference,
58951                     entityIds: [entity.id],
58952                     hash: JSON.stringify(tagSuggestingArea),
58953                     dynamicFixes: function(context) {
58954
58955                         var fixes = [];
58956
58957                         var entity = context.entity(this.entityIds[0]);
58958                         var connectEndsOnClick = makeConnectEndpointsFixOnClick(entity, context.graph());
58959
58960                         fixes.push(new validationIssueFix({
58961                             title: _t('issues.fix.connect_endpoints.title'),
58962                             onClick: connectEndsOnClick
58963                         }));
58964
58965                         fixes.push(new validationIssueFix({
58966                             icon: 'iD-operation-delete',
58967                             title: _t('issues.fix.remove_tag.title'),
58968                             onClick: function(context) {
58969                                 var entityId = this.issue.entityIds[0];
58970                                 var entity = context.entity(entityId);
58971                                 var tags = Object.assign({}, entity.tags);  // shallow copy
58972                                 for (var key in tagSuggestingArea) {
58973                                     delete tags[key];
58974                                 }
58975                                 context.perform(
58976                                     actionChangeTags(entityId, tags),
58977                                     _t('issues.fix.remove_tag.annotation')
58978                                 );
58979                             }
58980                         }));
58981
58982                         return fixes;
58983                     }
58984                 });
58985
58986
58987                 function showReference(selection) {
58988                     selection.selectAll('.issue-reference')
58989                         .data([0])
58990                         .enter()
58991                         .append('div')
58992                         .attr('class', 'issue-reference')
58993                         .text(_t('issues.tag_suggests_area.reference'));
58994                 }
58995             }
58996
58997             function vertexTaggedAsPointIssue(entity, graph) {
58998                 // we only care about nodes
58999                 if (entity.type !== 'node') { return null; }
59000
59001                 // ignore tagless points
59002                 if (Object.keys(entity.tags).length === 0) { return null; }
59003
59004                 // address lines are special so just ignore them
59005                 if (entity.isOnAddressLine(graph)) { return null; }
59006
59007                 var geometry = entity.geometry(graph);
59008                 var allowedGeometries = osmNodeGeometriesForTags(entity.tags);
59009
59010                 if (geometry === 'point' && !allowedGeometries.point && allowedGeometries.vertex) {
59011
59012                     return new validationIssue({
59013                         type: type,
59014                         subtype: 'vertex_as_point',
59015                         severity: 'warning',
59016                         message: function(context) {
59017                             var entity = context.hasEntity(this.entityIds[0]);
59018                             return entity ? _t('issues.vertex_as_point.message', {
59019                                 feature: utilDisplayLabel(entity, context.graph())
59020                             }) : '';
59021                         },
59022                         reference: function showReference(selection) {
59023                             selection.selectAll('.issue-reference')
59024                                 .data([0])
59025                                 .enter()
59026                                 .append('div')
59027                                 .attr('class', 'issue-reference')
59028                                 .text(_t('issues.vertex_as_point.reference'));
59029                         },
59030                         entityIds: [entity.id]
59031                     });
59032
59033                 } else if (geometry === 'vertex' && !allowedGeometries.vertex && allowedGeometries.point) {
59034
59035                     return new validationIssue({
59036                         type: type,
59037                         subtype: 'point_as_vertex',
59038                         severity: 'warning',
59039                         message: function(context) {
59040                             var entity = context.hasEntity(this.entityIds[0]);
59041                             return entity ? _t('issues.point_as_vertex.message', {
59042                                 feature: utilDisplayLabel(entity, context.graph())
59043                             }) : '';
59044                         },
59045                         reference: function showReference(selection) {
59046                             selection.selectAll('.issue-reference')
59047                                 .data([0])
59048                                 .enter()
59049                                 .append('div')
59050                                 .attr('class', 'issue-reference')
59051                                 .text(_t('issues.point_as_vertex.reference'));
59052                         },
59053                         entityIds: [entity.id],
59054                         dynamicFixes: function(context) {
59055
59056                             var entityId = this.entityIds[0];
59057
59058                             var extractOnClick = null;
59059                             if (!context.hasHiddenConnections(entityId)) {
59060
59061                                 extractOnClick = function(context) {
59062                                     var entityId = this.issue.entityIds[0];
59063                                     var action = actionExtract(entityId);
59064                                     context.perform(
59065                                         action,
59066                                         _t('operations.extract.annotation.single')
59067                                     );
59068                                     // re-enter mode to trigger updates
59069                                     context.enter(modeSelect(context, [action.getExtractedNodeID()]));
59070                                 };
59071                             }
59072
59073                             return [
59074                                 new validationIssueFix({
59075                                     icon: 'iD-operation-extract',
59076                                     title: _t('issues.fix.extract_point.title'),
59077                                     onClick: extractOnClick
59078                                 })
59079                             ];
59080                         }
59081                     });
59082                 }
59083
59084                 return null;
59085             }
59086
59087             function unclosedMultipolygonPartIssues(entity, graph) {
59088
59089                 if (entity.type !== 'relation' ||
59090                     !entity.isMultipolygon() ||
59091                     entity.isDegenerate() ||
59092                     // cannot determine issues for incompletely-downloaded relations
59093                     !entity.isComplete(graph)) { return null; }
59094
59095                 var sequences = osmJoinWays(entity.members, graph);
59096
59097                 var issues = [];
59098
59099                 for (var i in sequences) {
59100                     var sequence = sequences[i];
59101
59102                     if (!sequence.nodes) { continue; }
59103
59104                     var firstNode = sequence.nodes[0];
59105                     var lastNode = sequence.nodes[sequence.nodes.length - 1];
59106
59107                     // part is closed if the first and last nodes are the same
59108                     if (firstNode === lastNode) { continue; }
59109
59110                     var issue = new validationIssue({
59111                         type: type,
59112                         subtype: 'unclosed_multipolygon_part',
59113                         severity: 'warning',
59114                         message: function(context) {
59115                             var entity = context.hasEntity(this.entityIds[0]);
59116                             return entity ? _t('issues.unclosed_multipolygon_part.message', {
59117                                 feature: utilDisplayLabel(entity, context.graph())
59118                             }) : '';
59119                         },
59120                         reference: showReference,
59121                         loc: sequence.nodes[0].loc,
59122                         entityIds: [entity.id],
59123                         hash: sequence.map(function(way) {
59124                             return way.id;
59125                         }).join()
59126                     });
59127                     issues.push(issue);
59128                 }
59129
59130                 return issues;
59131
59132                 function showReference(selection) {
59133                     selection.selectAll('.issue-reference')
59134                         .data([0])
59135                         .enter()
59136                         .append('div')
59137                         .attr('class', 'issue-reference')
59138                         .text(_t('issues.unclosed_multipolygon_part.reference'));
59139                 }
59140             }
59141
59142             var validation = function checkMismatchedGeometry(entity, graph) {
59143                 var issues = [
59144                     vertexTaggedAsPointIssue(entity, graph),
59145                     lineTaggedAsAreaIssue(entity)
59146                 ];
59147                 issues = issues.concat(unclosedMultipolygonPartIssues(entity, graph));
59148                 return issues.filter(Boolean);
59149             };
59150
59151             validation.type = type;
59152
59153             return validation;
59154         }
59155
59156         function validationMissingRole() {
59157             var type = 'missing_role';
59158
59159             var validation = function checkMissingRole(entity, graph) {
59160                 var issues = [];
59161                 if (entity.type === 'way') {
59162                     graph.parentRelations(entity).forEach(function(relation) {
59163                         if (!relation.isMultipolygon()) { return; }
59164
59165                         var member = relation.memberById(entity.id);
59166                         if (member && isMissingRole(member)) {
59167                             issues.push(makeIssue(entity, relation, member));
59168                         }
59169                     });
59170                 } else if (entity.type === 'relation' && entity.isMultipolygon()) {
59171                     entity.indexedMembers().forEach(function(member) {
59172                         var way = graph.hasEntity(member.id);
59173                         if (way && isMissingRole(member)) {
59174                             issues.push(makeIssue(way, entity, member));
59175                         }
59176                     });
59177                 }
59178
59179                 return issues;
59180             };
59181
59182
59183             function isMissingRole(member) {
59184                 return !member.role || !member.role.trim().length;
59185             }
59186
59187
59188             function makeIssue(way, relation, member) {
59189                 return new validationIssue({
59190                     type: type,
59191                     severity: 'warning',
59192                     message: function(context) {
59193                         var member = context.hasEntity(this.entityIds[1]),
59194                             relation = context.hasEntity(this.entityIds[0]);
59195                         return (member && relation) ? _t('issues.missing_role.message', {
59196                             member: utilDisplayLabel(member, context.graph()),
59197                             relation: utilDisplayLabel(relation, context.graph())
59198                         }) : '';
59199                     },
59200                     reference: showReference,
59201                     entityIds: [relation.id, way.id],
59202                     data: {
59203                         member: member
59204                     },
59205                     hash: member.index.toString(),
59206                     dynamicFixes: function() {
59207                         return [
59208                             makeAddRoleFix('inner'),
59209                             makeAddRoleFix('outer'),
59210                             new validationIssueFix({
59211                                 icon: 'iD-operation-delete',
59212                                 title: _t('issues.fix.remove_from_relation.title'),
59213                                 onClick: function(context) {
59214                                     context.perform(
59215                                         actionDeleteMember(this.issue.entityIds[0], this.issue.data.member.index),
59216                                         _t('operations.delete_member.annotation')
59217                                     );
59218                                 }
59219                             })
59220                         ];
59221                     }
59222                 });
59223
59224
59225                 function showReference(selection) {
59226                     selection.selectAll('.issue-reference')
59227                         .data([0])
59228                         .enter()
59229                         .append('div')
59230                         .attr('class', 'issue-reference')
59231                         .text(_t('issues.missing_role.multipolygon.reference'));
59232                 }
59233             }
59234
59235
59236             function makeAddRoleFix(role) {
59237                 return new validationIssueFix({
59238                     title: _t('issues.fix.set_as_' + role + '.title'),
59239                     onClick: function(context) {
59240                         var oldMember = this.issue.data.member;
59241                         var member = { id: this.issue.entityIds[1], type: oldMember.type, role: role };
59242                         context.perform(
59243                             actionChangeMember(this.issue.entityIds[0], member, oldMember.index),
59244                             _t('operations.change_role.annotation')
59245                         );
59246                     }
59247                 });
59248             }
59249
59250             validation.type = type;
59251
59252             return validation;
59253         }
59254
59255         function validationMissingTag(context) {
59256             var type = 'missing_tag';
59257
59258             function hasDescriptiveTags(entity, graph) {
59259                 var keys = Object.keys(entity.tags)
59260                     .filter(function(k) {
59261                         if (k === 'area' || k === 'name') {
59262                             return false;
59263                         } else {
59264                             return osmIsInterestingTag(k);
59265                         }
59266                     });
59267
59268                 if (entity.type === 'relation' &&
59269                     keys.length === 1 &&
59270                     entity.tags.type === 'multipolygon') {
59271                     // this relation's only interesting tag just says its a multipolygon,
59272                     // which is not descriptive enough
59273
59274                     // It's okay for a simple multipolygon to have no descriptive tags
59275                     // if its outer way has them (old model, see `outdated_tags.js`)
59276                     return osmOldMultipolygonOuterMemberOfRelation(entity, graph);
59277                 }
59278
59279                 return keys.length > 0;
59280             }
59281
59282             function isUnknownRoad(entity) {
59283                 return entity.type === 'way' && entity.tags.highway === 'road';
59284             }
59285
59286             function isUntypedRelation(entity) {
59287                 return entity.type === 'relation' && !entity.tags.type;
59288             }
59289
59290             var validation = function checkMissingTag(entity, graph) {
59291
59292                 var subtype;
59293
59294                 var osm = context.connection();
59295                 var isUnloadedNode = entity.type === 'node' && osm && !osm.isDataLoaded(entity.loc);
59296
59297                 // we can't know if the node is a vertex if the tile is undownloaded
59298                 if (!isUnloadedNode &&
59299                     // allow untagged nodes that are part of ways
59300                     entity.geometry(graph) !== 'vertex' &&
59301                     // allow untagged entities that are part of relations
59302                     !entity.hasParentRelations(graph)) {
59303
59304                     if (Object.keys(entity.tags).length === 0) {
59305                         subtype = 'any';
59306                     } else if (!hasDescriptiveTags(entity, graph)) {
59307                         subtype = 'descriptive';
59308                     } else if (isUntypedRelation(entity)) {
59309                         subtype = 'relation_type';
59310                     }
59311                 }
59312
59313                 // flag an unknown road even if it's a member of a relation
59314                 if (!subtype && isUnknownRoad(entity)) {
59315                     subtype = 'highway_classification';
59316                 }
59317
59318                 if (!subtype) { return []; }
59319
59320                 var messageID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag.' + subtype;
59321                 var referenceID = subtype === 'highway_classification' ? 'unknown_road' : 'missing_tag';
59322
59323                 // can always delete if the user created it in the first place..
59324                 var canDelete = (entity.version === undefined || entity.v !== undefined);
59325                 var severity = (canDelete && subtype !== 'highway_classification') ? 'error' : 'warning';
59326
59327                 return [new validationIssue({
59328                     type: type,
59329                     subtype: subtype,
59330                     severity: severity,
59331                     message: function(context) {
59332                         var entity = context.hasEntity(this.entityIds[0]);
59333                         return entity ? _t('issues.' + messageID + '.message', {
59334                             feature: utilDisplayLabel(entity, context.graph())
59335                         }) : '';
59336                     },
59337                     reference: showReference,
59338                     entityIds: [entity.id],
59339                     dynamicFixes: function(context) {
59340
59341                         var fixes = [];
59342
59343                         var selectFixType = subtype === 'highway_classification' ? 'select_road_type' : 'select_preset';
59344
59345                         fixes.push(new validationIssueFix({
59346                             icon: 'iD-icon-search',
59347                             title: _t('issues.fix.' + selectFixType + '.title'),
59348                             onClick: function(context) {
59349                                 context.ui().sidebar.showPresetList();
59350                             }
59351                         }));
59352
59353                         var deleteOnClick;
59354
59355                         var id = this.entityIds[0];
59356                         var operation = operationDelete(context, [id]);
59357                         var disabledReasonID = operation.disabled();
59358                         if (!disabledReasonID) {
59359                             deleteOnClick = function(context) {
59360                                 var id = this.issue.entityIds[0];
59361                                 var operation = operationDelete(context, [id]);
59362                                 if (!operation.disabled()) {
59363                                     operation();
59364                                 }
59365                             };
59366                         }
59367
59368                         fixes.push(
59369                             new validationIssueFix({
59370                                 icon: 'iD-operation-delete',
59371                                 title: _t('issues.fix.delete_feature.title'),
59372                                 disabledReason: disabledReasonID ? _t('operations.delete.' + disabledReasonID + '.single') : undefined,
59373                                 onClick: deleteOnClick
59374                             })
59375                         );
59376
59377                         return fixes;
59378                     }
59379                 })];
59380
59381                 function showReference(selection) {
59382                     selection.selectAll('.issue-reference')
59383                         .data([0])
59384                         .enter()
59385                         .append('div')
59386                         .attr('class', 'issue-reference')
59387                         .text(_t('issues.' + referenceID + '.reference'));
59388                 }
59389             };
59390
59391             validation.type = type;
59392
59393             return validation;
59394         }
59395
59396         // remove spaces, punctuation, diacritics
59397         var simplify = function (str) {
59398           return diacritics.remove(
59399             str
59400               .replace(/&/g, 'and')
59401               .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,'')
59402               .toLowerCase()
59403           );
59404         };
59405
59406         // toParts - split a name-suggestion-index key into parts
59407         // {
59408         //   kvnd:        "amenity/fast_food|Thaï Express~(North America)",
59409         //   kvn:         "amenity/fast_food|Thaï Express",
59410         //   kv:          "amenity/fast_food",
59411         //   k:           "amenity",
59412         //   v:           "fast_food",
59413         //   n:           "Thaï Express",
59414         //   d:           "(North America)",
59415         //   nsimple:     "thaiexpress",
59416         //   kvnnsimple:  "amenity/fast_food|thaiexpress"
59417         // }
59418         var to_parts = function (kvnd) {
59419           var parts = {};
59420           parts.kvnd = kvnd;
59421
59422           var kvndparts = kvnd.split('~', 2);
59423           if (kvndparts.length > 1) { parts.d = kvndparts[1]; }
59424
59425           parts.kvn = kvndparts[0];
59426           var kvnparts = parts.kvn.split('|', 2);
59427           if (kvnparts.length > 1) { parts.n = kvnparts[1]; }
59428
59429           parts.kv = kvnparts[0];
59430           var kvparts = parts.kv.split('/', 2);
59431           parts.k = kvparts[0];
59432           parts.v = kvparts[1];
59433
59434           parts.nsimple = simplify(parts.n);
59435           parts.kvnsimple = parts.kv + '|' + parts.nsimple;
59436           return parts;
59437         };
59438
59439         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"]};
59440         var match_groups = {
59441         matchGroups: matchGroups
59442         };
59443
59444         var match_groups$1 = /*#__PURE__*/Object.freeze({
59445                 __proto__: null,
59446                 matchGroups: matchGroups,
59447                 'default': match_groups
59448         });
59449
59450         var require$$0 = getCjsExportFromNamespace(match_groups$1);
59451
59452         var matchGroups$1 = require$$0.matchGroups;
59453
59454
59455         var matcher$1 = function () {
59456           var _warnings = [];  // array of match conflict pairs
59457           var _ambiguous = {};
59458           var _matchIndex = {};
59459           var matcher = {};
59460
59461
59462           // Create an index of all the keys/simplenames for fast matching
59463           matcher.buildMatchIndex = function (brands) {
59464             // two passes - once for primary names, once for secondary/alternate names
59465             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'primary'); });
59466             Object.keys(brands).forEach(function (kvnd) { return insertNames(kvnd, 'secondary'); });
59467
59468
59469             function insertNames(kvnd, which) {
59470               var obj = brands[kvnd];
59471               var parts = to_parts(kvnd);
59472
59473               // Exit early for ambiguous names in the second pass.
59474               // They were collected in the first pass and we don't gather alt names for them.
59475               if (which === 'secondary' && parts.d) { return; }
59476
59477
59478               if (obj.countryCodes) {
59479                 parts.countryCodes = obj.countryCodes.slice();  // copy
59480               }
59481
59482               var nomatches = (obj.nomatch || []);
59483               if (nomatches.some(function (s) { return s === kvnd; })) {
59484                 console.log(("WARNING match/nomatch conflict for " + kvnd));
59485                 return;
59486               }
59487
59488               var match_kv = [parts.kv]
59489                 .concat(obj.matchTags || [])
59490                 .concat([((parts.k) + "/yes"), "building/yes"])   // #3454 - match some generic tags
59491                 .map(function (s) { return s.toLowerCase(); });
59492
59493               var match_nsimple = [];
59494               if (which === 'primary') {
59495                 match_nsimple = [parts.n]
59496                   .concat(obj.matchNames || [])
59497                   .concat(obj.tags.official_name || [])   // #2732 - match alternate names
59498                   .map(simplify);
59499
59500               } else if (which === 'secondary') {
59501                 match_nsimple = []
59502                   .concat(obj.tags.alt_name || [])        // #2732 - match alternate names
59503                   .concat(obj.tags.short_name || [])      // #2732 - match alternate names
59504                   .map(simplify);
59505               }
59506
59507               if (!match_nsimple.length) { return; }  // nothing to do
59508
59509               match_kv.forEach(function (kv) {
59510                 match_nsimple.forEach(function (nsimple) {
59511                   if (parts.d) {
59512                     // Known ambiguous names with disambiguation string ~(USA) / ~(Canada)
59513                     // FIXME: Name collisions will overwrite the initial entry (ok for now)
59514                     if (!_ambiguous[kv]) { _ambiguous[kv] = {}; }
59515                     _ambiguous[kv][nsimple] = parts;
59516
59517                   } else {
59518                     // Names we mostly expect to be unique..
59519                     if (!_matchIndex[kv]) { _matchIndex[kv] = {}; }
59520
59521                     var m = _matchIndex[kv][nsimple];
59522                     if (m) {  // There already is a match for this name, skip it
59523                       // Warn if we detect collisions in a primary name.
59524                       // Skip warning if a secondary name or a generic `*=yes` tag - #2972 / #3454
59525                       if (which === 'primary' && !/\/yes$/.test(kv)) {
59526                         _warnings.push([m.kvnd, (kvnd + " (" + kv + "/" + nsimple + ")")]);
59527                       }
59528                     } else {
59529                       _matchIndex[kv][nsimple] = parts;   // insert
59530                     }
59531                   }
59532                 });
59533               });
59534
59535             }
59536           };
59537
59538
59539           // pass a `key`, `value`, `name` and return the best match,
59540           // `countryCode` optional (if supplied, must match that too)
59541           matcher.matchKVN = function (key, value, name, countryCode) {
59542             return matcher.matchParts(to_parts((key + "/" + value + "|" + name)), countryCode);
59543           };
59544
59545
59546           // pass a parts object and return the best match,
59547           // `countryCode` optional (if supplied, must match that too)
59548           matcher.matchParts = function (parts, countryCode) {
59549             var match = null;
59550             var inGroup = false;
59551
59552             // fixme: we currently return a single match for ambiguous
59553             match = _ambiguous[parts.kv] && _ambiguous[parts.kv][parts.nsimple];
59554             if (match && matchesCountryCode(match)) { return match; }
59555
59556             // try to return an exact match
59557             match = _matchIndex[parts.kv] && _matchIndex[parts.kv][parts.nsimple];
59558             if (match && matchesCountryCode(match)) { return match; }
59559
59560             // look in match groups
59561             for (var mg in matchGroups$1) {
59562               var matchGroup = matchGroups$1[mg];
59563               match = null;
59564               inGroup = false;
59565
59566               for (var i = 0; i < matchGroup.length; i++) {
59567                 var otherkv = matchGroup[i].toLowerCase();
59568                 if (!inGroup) {
59569                   inGroup = otherkv === parts.kv;
59570                 }
59571                 if (!match) {
59572                   // fixme: we currently return a single match for ambiguous
59573                   match = _ambiguous[otherkv] && _ambiguous[otherkv][parts.nsimple];
59574                 }
59575                 if (!match) {
59576                   match = _matchIndex[otherkv] && _matchIndex[otherkv][parts.nsimple];
59577                 }
59578
59579                 if (match && !matchesCountryCode(match)) {
59580                   match = null;
59581                 }
59582
59583                 if (inGroup && match) {
59584                   return match;
59585                 }
59586               }
59587             }
59588
59589             return null;
59590
59591             function matchesCountryCode(match) {
59592               if (!countryCode) { return true; }
59593               if (!match.countryCodes) { return true; }
59594               return match.countryCodes.indexOf(countryCode) !== -1;
59595             }
59596           };
59597
59598
59599           matcher.getWarnings = function () { return _warnings; };
59600
59601           return matcher;
59602         };
59603
59604         var quickselect$1 = createCommonjsModule(function (module, exports) {
59605         (function (global, factory) {
59606                  module.exports = factory() ;
59607         }(commonjsGlobal, (function () {
59608         function quickselect(arr, k, left, right, compare) {
59609             quickselectStep(arr, k, left || 0, right || (arr.length - 1), compare || defaultCompare);
59610         }
59611
59612         function quickselectStep(arr, k, left, right, compare) {
59613
59614             while (right > left) {
59615                 if (right - left > 600) {
59616                     var n = right - left + 1;
59617                     var m = k - left + 1;
59618                     var z = Math.log(n);
59619                     var s = 0.5 * Math.exp(2 * z / 3);
59620                     var sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);
59621                     var newLeft = Math.max(left, Math.floor(k - m * s / n + sd));
59622                     var newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));
59623                     quickselectStep(arr, k, newLeft, newRight, compare);
59624                 }
59625
59626                 var t = arr[k];
59627                 var i = left;
59628                 var j = right;
59629
59630                 swap(arr, left, k);
59631                 if (compare(arr[right], t) > 0) { swap(arr, left, right); }
59632
59633                 while (i < j) {
59634                     swap(arr, i, j);
59635                     i++;
59636                     j--;
59637                     while (compare(arr[i], t) < 0) { i++; }
59638                     while (compare(arr[j], t) > 0) { j--; }
59639                 }
59640
59641                 if (compare(arr[left], t) === 0) { swap(arr, left, j); }
59642                 else {
59643                     j++;
59644                     swap(arr, j, right);
59645                 }
59646
59647                 if (j <= k) { left = j + 1; }
59648                 if (k <= j) { right = j - 1; }
59649             }
59650         }
59651
59652         function swap(arr, i, j) {
59653             var tmp = arr[i];
59654             arr[i] = arr[j];
59655             arr[j] = tmp;
59656         }
59657
59658         function defaultCompare(a, b) {
59659             return a < b ? -1 : a > b ? 1 : 0;
59660         }
59661
59662         return quickselect;
59663
59664         })));
59665         });
59666
59667         var rbush_1 = rbush;
59668         var _default$2 = rbush;
59669
59670
59671
59672         function rbush(maxEntries, format) {
59673             if (!(this instanceof rbush)) { return new rbush(maxEntries, format); }
59674
59675             // max entries in a node is 9 by default; min node fill is 40% for best performance
59676             this._maxEntries = Math.max(4, maxEntries || 9);
59677             this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));
59678
59679             if (format) {
59680                 this._initFormat(format);
59681             }
59682
59683             this.clear();
59684         }
59685
59686         rbush.prototype = {
59687
59688             all: function () {
59689                 return this._all(this.data, []);
59690             },
59691
59692             search: function (bbox) {
59693
59694                 var node = this.data,
59695                     result = [],
59696                     toBBox = this.toBBox;
59697
59698                 if (!intersects$1(bbox, node)) { return result; }
59699
59700                 var nodesToSearch = [],
59701                     i, len, child, childBBox;
59702
59703                 while (node) {
59704                     for (i = 0, len = node.children.length; i < len; i++) {
59705
59706                         child = node.children[i];
59707                         childBBox = node.leaf ? toBBox(child) : child;
59708
59709                         if (intersects$1(bbox, childBBox)) {
59710                             if (node.leaf) { result.push(child); }
59711                             else if (contains$2(bbox, childBBox)) { this._all(child, result); }
59712                             else { nodesToSearch.push(child); }
59713                         }
59714                     }
59715                     node = nodesToSearch.pop();
59716                 }
59717
59718                 return result;
59719             },
59720
59721             collides: function (bbox) {
59722
59723                 var node = this.data,
59724                     toBBox = this.toBBox;
59725
59726                 if (!intersects$1(bbox, node)) { return false; }
59727
59728                 var nodesToSearch = [],
59729                     i, len, child, childBBox;
59730
59731                 while (node) {
59732                     for (i = 0, len = node.children.length; i < len; i++) {
59733
59734                         child = node.children[i];
59735                         childBBox = node.leaf ? toBBox(child) : child;
59736
59737                         if (intersects$1(bbox, childBBox)) {
59738                             if (node.leaf || contains$2(bbox, childBBox)) { return true; }
59739                             nodesToSearch.push(child);
59740                         }
59741                     }
59742                     node = nodesToSearch.pop();
59743                 }
59744
59745                 return false;
59746             },
59747
59748             load: function (data) {
59749                 if (!(data && data.length)) { return this; }
59750
59751                 if (data.length < this._minEntries) {
59752                     for (var i = 0, len = data.length; i < len; i++) {
59753                         this.insert(data[i]);
59754                     }
59755                     return this;
59756                 }
59757
59758                 // recursively build the tree with the given data from scratch using OMT algorithm
59759                 var node = this._build(data.slice(), 0, data.length - 1, 0);
59760
59761                 if (!this.data.children.length) {
59762                     // save as is if tree is empty
59763                     this.data = node;
59764
59765                 } else if (this.data.height === node.height) {
59766                     // split root if trees have the same height
59767                     this._splitRoot(this.data, node);
59768
59769                 } else {
59770                     if (this.data.height < node.height) {
59771                         // swap trees if inserted one is bigger
59772                         var tmpNode = this.data;
59773                         this.data = node;
59774                         node = tmpNode;
59775                     }
59776
59777                     // insert the small tree into the large tree at appropriate level
59778                     this._insert(node, this.data.height - node.height - 1, true);
59779                 }
59780
59781                 return this;
59782             },
59783
59784             insert: function (item) {
59785                 if (item) { this._insert(item, this.data.height - 1); }
59786                 return this;
59787             },
59788
59789             clear: function () {
59790                 this.data = createNode$1([]);
59791                 return this;
59792             },
59793
59794             remove: function (item, equalsFn) {
59795                 if (!item) { return this; }
59796
59797                 var node = this.data,
59798                     bbox = this.toBBox(item),
59799                     path = [],
59800                     indexes = [],
59801                     i, parent, index, goingUp;
59802
59803                 // depth-first iterative tree traversal
59804                 while (node || path.length) {
59805
59806                     if (!node) { // go up
59807                         node = path.pop();
59808                         parent = path[path.length - 1];
59809                         i = indexes.pop();
59810                         goingUp = true;
59811                     }
59812
59813                     if (node.leaf) { // check current node
59814                         index = findItem$1(item, node.children, equalsFn);
59815
59816                         if (index !== -1) {
59817                             // item found, remove the item and condense tree upwards
59818                             node.children.splice(index, 1);
59819                             path.push(node);
59820                             this._condense(path);
59821                             return this;
59822                         }
59823                     }
59824
59825                     if (!goingUp && !node.leaf && contains$2(node, bbox)) { // go down
59826                         path.push(node);
59827                         indexes.push(i);
59828                         i = 0;
59829                         parent = node;
59830                         node = node.children[0];
59831
59832                     } else if (parent) { // go right
59833                         i++;
59834                         node = parent.children[i];
59835                         goingUp = false;
59836
59837                     } else { node = null; } // nothing found
59838                 }
59839
59840                 return this;
59841             },
59842
59843             toBBox: function (item) { return item; },
59844
59845             compareMinX: compareNodeMinX$1,
59846             compareMinY: compareNodeMinY$1,
59847
59848             toJSON: function () { return this.data; },
59849
59850             fromJSON: function (data) {
59851                 this.data = data;
59852                 return this;
59853             },
59854
59855             _all: function (node, result) {
59856                 var nodesToSearch = [];
59857                 while (node) {
59858                     if (node.leaf) { result.push.apply(result, node.children); }
59859                     else { nodesToSearch.push.apply(nodesToSearch, node.children); }
59860
59861                     node = nodesToSearch.pop();
59862                 }
59863                 return result;
59864             },
59865
59866             _build: function (items, left, right, height) {
59867
59868                 var N = right - left + 1,
59869                     M = this._maxEntries,
59870                     node;
59871
59872                 if (N <= M) {
59873                     // reached leaf level; return leaf
59874                     node = createNode$1(items.slice(left, right + 1));
59875                     calcBBox$1(node, this.toBBox);
59876                     return node;
59877                 }
59878
59879                 if (!height) {
59880                     // target height of the bulk-loaded tree
59881                     height = Math.ceil(Math.log(N) / Math.log(M));
59882
59883                     // target number of root entries to maximize storage utilization
59884                     M = Math.ceil(N / Math.pow(M, height - 1));
59885                 }
59886
59887                 node = createNode$1([]);
59888                 node.leaf = false;
59889                 node.height = height;
59890
59891                 // split the items into M mostly square tiles
59892
59893                 var N2 = Math.ceil(N / M),
59894                     N1 = N2 * Math.ceil(Math.sqrt(M)),
59895                     i, j, right2, right3;
59896
59897                 multiSelect$1(items, left, right, N1, this.compareMinX);
59898
59899                 for (i = left; i <= right; i += N1) {
59900
59901                     right2 = Math.min(i + N1 - 1, right);
59902
59903                     multiSelect$1(items, i, right2, N2, this.compareMinY);
59904
59905                     for (j = i; j <= right2; j += N2) {
59906
59907                         right3 = Math.min(j + N2 - 1, right2);
59908
59909                         // pack each entry recursively
59910                         node.children.push(this._build(items, j, right3, height - 1));
59911                     }
59912                 }
59913
59914                 calcBBox$1(node, this.toBBox);
59915
59916                 return node;
59917             },
59918
59919             _chooseSubtree: function (bbox, node, level, path) {
59920
59921                 var i, len, child, targetNode, area, enlargement, minArea, minEnlargement;
59922
59923                 while (true) {
59924                     path.push(node);
59925
59926                     if (node.leaf || path.length - 1 === level) { break; }
59927
59928                     minArea = minEnlargement = Infinity;
59929
59930                     for (i = 0, len = node.children.length; i < len; i++) {
59931                         child = node.children[i];
59932                         area = bboxArea$1(child);
59933                         enlargement = enlargedArea$1(bbox, child) - area;
59934
59935                         // choose entry with the least area enlargement
59936                         if (enlargement < minEnlargement) {
59937                             minEnlargement = enlargement;
59938                             minArea = area < minArea ? area : minArea;
59939                             targetNode = child;
59940
59941                         } else if (enlargement === minEnlargement) {
59942                             // otherwise choose one with the smallest area
59943                             if (area < minArea) {
59944                                 minArea = area;
59945                                 targetNode = child;
59946                             }
59947                         }
59948                     }
59949
59950                     node = targetNode || node.children[0];
59951                 }
59952
59953                 return node;
59954             },
59955
59956             _insert: function (item, level, isNode) {
59957
59958                 var toBBox = this.toBBox,
59959                     bbox = isNode ? item : toBBox(item),
59960                     insertPath = [];
59961
59962                 // find the best node for accommodating the item, saving all nodes along the path too
59963                 var node = this._chooseSubtree(bbox, this.data, level, insertPath);
59964
59965                 // put the item into the node
59966                 node.children.push(item);
59967                 extend$3(node, bbox);
59968
59969                 // split on node overflow; propagate upwards if necessary
59970                 while (level >= 0) {
59971                     if (insertPath[level].children.length > this._maxEntries) {
59972                         this._split(insertPath, level);
59973                         level--;
59974                     } else { break; }
59975                 }
59976
59977                 // adjust bboxes along the insertion path
59978                 this._adjustParentBBoxes(bbox, insertPath, level);
59979             },
59980
59981             // split overflowed node into two
59982             _split: function (insertPath, level) {
59983
59984                 var node = insertPath[level],
59985                     M = node.children.length,
59986                     m = this._minEntries;
59987
59988                 this._chooseSplitAxis(node, m, M);
59989
59990                 var splitIndex = this._chooseSplitIndex(node, m, M);
59991
59992                 var newNode = createNode$1(node.children.splice(splitIndex, node.children.length - splitIndex));
59993                 newNode.height = node.height;
59994                 newNode.leaf = node.leaf;
59995
59996                 calcBBox$1(node, this.toBBox);
59997                 calcBBox$1(newNode, this.toBBox);
59998
59999                 if (level) { insertPath[level - 1].children.push(newNode); }
60000                 else { this._splitRoot(node, newNode); }
60001             },
60002
60003             _splitRoot: function (node, newNode) {
60004                 // split root node
60005                 this.data = createNode$1([node, newNode]);
60006                 this.data.height = node.height + 1;
60007                 this.data.leaf = false;
60008                 calcBBox$1(this.data, this.toBBox);
60009             },
60010
60011             _chooseSplitIndex: function (node, m, M) {
60012
60013                 var i, bbox1, bbox2, overlap, area, minOverlap, minArea, index;
60014
60015                 minOverlap = minArea = Infinity;
60016
60017                 for (i = m; i <= M - m; i++) {
60018                     bbox1 = distBBox$1(node, 0, i, this.toBBox);
60019                     bbox2 = distBBox$1(node, i, M, this.toBBox);
60020
60021                     overlap = intersectionArea$1(bbox1, bbox2);
60022                     area = bboxArea$1(bbox1) + bboxArea$1(bbox2);
60023
60024                     // choose distribution with minimum overlap
60025                     if (overlap < minOverlap) {
60026                         minOverlap = overlap;
60027                         index = i;
60028
60029                         minArea = area < minArea ? area : minArea;
60030
60031                     } else if (overlap === minOverlap) {
60032                         // otherwise choose distribution with minimum area
60033                         if (area < minArea) {
60034                             minArea = area;
60035                             index = i;
60036                         }
60037                     }
60038                 }
60039
60040                 return index;
60041             },
60042
60043             // sorts node children by the best axis for split
60044             _chooseSplitAxis: function (node, m, M) {
60045
60046                 var compareMinX = node.leaf ? this.compareMinX : compareNodeMinX$1,
60047                     compareMinY = node.leaf ? this.compareMinY : compareNodeMinY$1,
60048                     xMargin = this._allDistMargin(node, m, M, compareMinX),
60049                     yMargin = this._allDistMargin(node, m, M, compareMinY);
60050
60051                 // if total distributions margin value is minimal for x, sort by minX,
60052                 // otherwise it's already sorted by minY
60053                 if (xMargin < yMargin) { node.children.sort(compareMinX); }
60054             },
60055
60056             // total margin of all possible split distributions where each node is at least m full
60057             _allDistMargin: function (node, m, M, compare) {
60058
60059                 node.children.sort(compare);
60060
60061                 var toBBox = this.toBBox,
60062                     leftBBox = distBBox$1(node, 0, m, toBBox),
60063                     rightBBox = distBBox$1(node, M - m, M, toBBox),
60064                     margin = bboxMargin$1(leftBBox) + bboxMargin$1(rightBBox),
60065                     i, child;
60066
60067                 for (i = m; i < M - m; i++) {
60068                     child = node.children[i];
60069                     extend$3(leftBBox, node.leaf ? toBBox(child) : child);
60070                     margin += bboxMargin$1(leftBBox);
60071                 }
60072
60073                 for (i = M - m - 1; i >= m; i--) {
60074                     child = node.children[i];
60075                     extend$3(rightBBox, node.leaf ? toBBox(child) : child);
60076                     margin += bboxMargin$1(rightBBox);
60077                 }
60078
60079                 return margin;
60080             },
60081
60082             _adjustParentBBoxes: function (bbox, path, level) {
60083                 // adjust bboxes along the given tree path
60084                 for (var i = level; i >= 0; i--) {
60085                     extend$3(path[i], bbox);
60086                 }
60087             },
60088
60089             _condense: function (path) {
60090                 // go through the path, removing empty nodes and updating bboxes
60091                 for (var i = path.length - 1, siblings; i >= 0; i--) {
60092                     if (path[i].children.length === 0) {
60093                         if (i > 0) {
60094                             siblings = path[i - 1].children;
60095                             siblings.splice(siblings.indexOf(path[i]), 1);
60096
60097                         } else { this.clear(); }
60098
60099                     } else { calcBBox$1(path[i], this.toBBox); }
60100                 }
60101             },
60102
60103             _initFormat: function (format) {
60104                 // data format (minX, minY, maxX, maxY accessors)
60105
60106                 // uses eval-type function compilation instead of just accepting a toBBox function
60107                 // because the algorithms are very sensitive to sorting functions performance,
60108                 // so they should be dead simple and without inner calls
60109
60110                 var compareArr = ['return a', ' - b', ';'];
60111
60112                 this.compareMinX = new Function('a', 'b', compareArr.join(format[0]));
60113                 this.compareMinY = new Function('a', 'b', compareArr.join(format[1]));
60114
60115                 this.toBBox = new Function('a',
60116                     'return {minX: a' + format[0] +
60117                     ', minY: a' + format[1] +
60118                     ', maxX: a' + format[2] +
60119                     ', maxY: a' + format[3] + '};');
60120             }
60121         };
60122
60123         function findItem$1(item, items, equalsFn) {
60124             if (!equalsFn) { return items.indexOf(item); }
60125
60126             for (var i = 0; i < items.length; i++) {
60127                 if (equalsFn(item, items[i])) { return i; }
60128             }
60129             return -1;
60130         }
60131
60132         // calculate node's bbox from bboxes of its children
60133         function calcBBox$1(node, toBBox) {
60134             distBBox$1(node, 0, node.children.length, toBBox, node);
60135         }
60136
60137         // min bounding rectangle of node children from k to p-1
60138         function distBBox$1(node, k, p, toBBox, destNode) {
60139             if (!destNode) { destNode = createNode$1(null); }
60140             destNode.minX = Infinity;
60141             destNode.minY = Infinity;
60142             destNode.maxX = -Infinity;
60143             destNode.maxY = -Infinity;
60144
60145             for (var i = k, child; i < p; i++) {
60146                 child = node.children[i];
60147                 extend$3(destNode, node.leaf ? toBBox(child) : child);
60148             }
60149
60150             return destNode;
60151         }
60152
60153         function extend$3(a, b) {
60154             a.minX = Math.min(a.minX, b.minX);
60155             a.minY = Math.min(a.minY, b.minY);
60156             a.maxX = Math.max(a.maxX, b.maxX);
60157             a.maxY = Math.max(a.maxY, b.maxY);
60158             return a;
60159         }
60160
60161         function compareNodeMinX$1(a, b) { return a.minX - b.minX; }
60162         function compareNodeMinY$1(a, b) { return a.minY - b.minY; }
60163
60164         function bboxArea$1(a)   { return (a.maxX - a.minX) * (a.maxY - a.minY); }
60165         function bboxMargin$1(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }
60166
60167         function enlargedArea$1(a, b) {
60168             return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *
60169                    (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));
60170         }
60171
60172         function intersectionArea$1(a, b) {
60173             var minX = Math.max(a.minX, b.minX),
60174                 minY = Math.max(a.minY, b.minY),
60175                 maxX = Math.min(a.maxX, b.maxX),
60176                 maxY = Math.min(a.maxY, b.maxY);
60177
60178             return Math.max(0, maxX - minX) *
60179                    Math.max(0, maxY - minY);
60180         }
60181
60182         function contains$2(a, b) {
60183             return a.minX <= b.minX &&
60184                    a.minY <= b.minY &&
60185                    b.maxX <= a.maxX &&
60186                    b.maxY <= a.maxY;
60187         }
60188
60189         function intersects$1(a, b) {
60190             return b.minX <= a.maxX &&
60191                    b.minY <= a.maxY &&
60192                    b.maxX >= a.minX &&
60193                    b.maxY >= a.minY;
60194         }
60195
60196         function createNode$1(children) {
60197             return {
60198                 children: children,
60199                 height: 1,
60200                 leaf: true,
60201                 minX: Infinity,
60202                 minY: Infinity,
60203                 maxX: -Infinity,
60204                 maxY: -Infinity
60205             };
60206         }
60207
60208         // sort an array so that items come in groups of n unsorted items, with groups sorted between each other;
60209         // combines selection algorithm with binary divide & conquer approach
60210
60211         function multiSelect$1(arr, left, right, n, compare) {
60212             var stack = [left, right],
60213                 mid;
60214
60215             while (stack.length) {
60216                 right = stack.pop();
60217                 left = stack.pop();
60218
60219                 if (right - left <= n) { continue; }
60220
60221                 mid = left + Math.ceil((right - left) / n / 2) * n;
60222                 quickselect$1(arr, mid, left, right, compare);
60223
60224                 stack.push(left, mid, mid, right);
60225             }
60226         }
60227         rbush_1.default = _default$2;
60228
60229         var lineclip_1$1 = lineclip$1;
60230
60231         lineclip$1.polyline = lineclip$1;
60232         lineclip$1.polygon = polygonclip$1;
60233
60234
60235         // Cohen-Sutherland line clippign algorithm, adapted to efficiently
60236         // handle polylines rather than just segments
60237
60238         function lineclip$1(points, bbox, result) {
60239
60240             var len = points.length,
60241                 codeA = bitCode$1(points[0], bbox),
60242                 part = [],
60243                 i, a, b, codeB, lastCode;
60244
60245             if (!result) { result = []; }
60246
60247             for (i = 1; i < len; i++) {
60248                 a = points[i - 1];
60249                 b = points[i];
60250                 codeB = lastCode = bitCode$1(b, bbox);
60251
60252                 while (true) {
60253
60254                     if (!(codeA | codeB)) { // accept
60255                         part.push(a);
60256
60257                         if (codeB !== lastCode) { // segment went outside
60258                             part.push(b);
60259
60260                             if (i < len - 1) { // start a new line
60261                                 result.push(part);
60262                                 part = [];
60263                             }
60264                         } else if (i === len - 1) {
60265                             part.push(b);
60266                         }
60267                         break;
60268
60269                     } else if (codeA & codeB) { // trivial reject
60270                         break;
60271
60272                     } else if (codeA) { // a outside, intersect with clip edge
60273                         a = intersect$1(a, b, codeA, bbox);
60274                         codeA = bitCode$1(a, bbox);
60275
60276                     } else { // b outside
60277                         b = intersect$1(a, b, codeB, bbox);
60278                         codeB = bitCode$1(b, bbox);
60279                     }
60280                 }
60281
60282                 codeA = lastCode;
60283             }
60284
60285             if (part.length) { result.push(part); }
60286
60287             return result;
60288         }
60289
60290         // Sutherland-Hodgeman polygon clipping algorithm
60291
60292         function polygonclip$1(points, bbox) {
60293
60294             var result, edge, prev, prevInside, i, p, inside;
60295
60296             // clip against each side of the clip rectangle
60297             for (edge = 1; edge <= 8; edge *= 2) {
60298                 result = [];
60299                 prev = points[points.length - 1];
60300                 prevInside = !(bitCode$1(prev, bbox) & edge);
60301
60302                 for (i = 0; i < points.length; i++) {
60303                     p = points[i];
60304                     inside = !(bitCode$1(p, bbox) & edge);
60305
60306                     // if segment goes through the clip window, add an intersection
60307                     if (inside !== prevInside) { result.push(intersect$1(prev, p, edge, bbox)); }
60308
60309                     if (inside) { result.push(p); } // add a point if it's inside
60310
60311                     prev = p;
60312                     prevInside = inside;
60313                 }
60314
60315                 points = result;
60316
60317                 if (!points.length) { break; }
60318             }
60319
60320             return result;
60321         }
60322
60323         // intersect a segment against one of the 4 lines that make up the bbox
60324
60325         function intersect$1(a, b, edge, bbox) {
60326             return edge & 8 ? [a[0] + (b[0] - a[0]) * (bbox[3] - a[1]) / (b[1] - a[1]), bbox[3]] : // top
60327                    edge & 4 ? [a[0] + (b[0] - a[0]) * (bbox[1] - a[1]) / (b[1] - a[1]), bbox[1]] : // bottom
60328                    edge & 2 ? [bbox[2], a[1] + (b[1] - a[1]) * (bbox[2] - a[0]) / (b[0] - a[0])] : // right
60329                    edge & 1 ? [bbox[0], a[1] + (b[1] - a[1]) * (bbox[0] - a[0]) / (b[0] - a[0])] : // left
60330                    null;
60331         }
60332
60333         // bit code reflects the point position relative to the bbox:
60334
60335         //         left  mid  right
60336         //    top  1001  1000  1010
60337         //    mid  0001  0000  0010
60338         // bottom  0101  0100  0110
60339
60340         function bitCode$1(p, bbox) {
60341             var code = 0;
60342
60343             if (p[0] < bbox[0]) { code |= 1; } // left
60344             else if (p[0] > bbox[2]) { code |= 2; } // right
60345
60346             if (p[1] < bbox[1]) { code |= 4; } // bottom
60347             else if (p[1] > bbox[3]) { code |= 8; } // top
60348
60349             return code;
60350         }
60351
60352         var whichPolygon_1 = whichPolygon;
60353
60354         function whichPolygon(data) {
60355             var bboxes = [];
60356             for (var i = 0; i < data.features.length; i++) {
60357                 var feature = data.features[i];
60358                 var coords = feature.geometry.coordinates;
60359
60360                 if (feature.geometry.type === 'Polygon') {
60361                     bboxes.push(treeItem(coords, feature.properties));
60362
60363                 } else if (feature.geometry.type === 'MultiPolygon') {
60364                     for (var j = 0; j < coords.length; j++) {
60365                         bboxes.push(treeItem(coords[j], feature.properties));
60366                     }
60367                 }
60368             }
60369
60370             var tree = rbush_1().load(bboxes);
60371
60372             function query(p, multi) {
60373                 var output = [],
60374                     result = tree.search({
60375                         minX: p[0],
60376                         minY: p[1],
60377                         maxX: p[0],
60378                         maxY: p[1]
60379                     });
60380                 for (var i = 0; i < result.length; i++) {
60381                     if (insidePolygon(result[i].coords, p)) {
60382                         if (multi)
60383                             { output.push(result[i].props); }
60384                         else
60385                             { return result[i].props; }
60386                     }
60387                 }
60388                 return multi && output.length ? output : null;
60389             }
60390
60391             query.tree = tree;
60392             query.bbox = function queryBBox(bbox) {
60393                 var output = [];
60394                 var result = tree.search({
60395                     minX: bbox[0],
60396                     minY: bbox[1],
60397                     maxX: bbox[2],
60398                     maxY: bbox[3]
60399                 });
60400                 for (var i = 0; i < result.length; i++) {
60401                     if (polygonIntersectsBBox(result[i].coords, bbox)) {
60402                         output.push(result[i].props);
60403                     }
60404                 }
60405                 return output;
60406             };
60407
60408             return query;
60409         }
60410
60411         function polygonIntersectsBBox(polygon, bbox) {
60412             var bboxCenter = [
60413                 (bbox[0] + bbox[2]) / 2,
60414                 (bbox[1] + bbox[3]) / 2
60415             ];
60416             if (insidePolygon(polygon, bboxCenter)) { return true; }
60417             for (var i = 0; i < polygon.length; i++) {
60418                 if (lineclip_1$1(polygon[i], bbox).length > 0) { return true; }
60419             }
60420             return false;
60421         }
60422
60423         // ray casting algorithm for detecting if point is in polygon
60424         function insidePolygon(rings, p) {
60425             var inside = false;
60426             for (var i = 0, len = rings.length; i < len; i++) {
60427                 var ring = rings[i];
60428                 for (var j = 0, len2 = ring.length, k = len2 - 1; j < len2; k = j++) {
60429                     if (rayIntersect(p, ring[j], ring[k])) { inside = !inside; }
60430                 }
60431             }
60432             return inside;
60433         }
60434
60435         function rayIntersect(p, p1, p2) {
60436             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]);
60437         }
60438
60439         function treeItem(coords, props) {
60440             var item = {
60441                 minX: Infinity,
60442                 minY: Infinity,
60443                 maxX: -Infinity,
60444                 maxY: -Infinity,
60445                 coords: coords,
60446                 props: props
60447             };
60448
60449             for (var i = 0; i < coords[0].length; i++) {
60450                 var p = coords[0][i];
60451                 item.minX = Math.min(item.minX, p[0]);
60452                 item.minY = Math.min(item.minY, p[1]);
60453                 item.maxX = Math.max(item.maxX, p[0]);
60454                 item.maxY = Math.max(item.maxY, p[1]);
60455             }
60456             return item;
60457         }
60458
60459         var type = "FeatureCollection";
60460         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]]]]}}];
60461         var rawBorders = {
60462         type: type,
60463         features: features
60464         };
60465
60466         var borders = rawBorders;
60467         var whichPolygonGetter = {};
60468         var featuresByCode = {};
60469         var idFilterRegex = /\bThe\b|\bthe\b|\band\b|\bof\b|[-_ .,()&[\]/]/g;
60470         var levels = [
60471           'subterritory',
60472           'territory',
60473           'country',
60474           'intermediateRegion',
60475           'subregion',
60476           'region',
60477           'union',
60478           'world'
60479         ];
60480         loadDerivedDataAndCaches(borders);
60481         function loadDerivedDataAndCaches(borders) {
60482           var identifierProps = ['iso1A2', 'iso1A3', 'm49', 'wikidata', 'emojiFlag', 'nameEn'];
60483           var geometryFeatures = [];
60484           for (var i in borders.features) {
60485             var feature = borders.features[i];
60486             feature.properties.id = feature.properties.iso1A2 || feature.properties.m49;
60487             loadM49(feature);
60488             loadIsoStatus(feature);
60489             loadLevel(feature);
60490             loadGroups(feature);
60491             loadRoadSpeedUnit(feature);
60492             loadDriveSide(feature);
60493             loadFlag(feature);
60494             cacheFeatureByIDs(feature);
60495             if (feature.geometry) { geometryFeatures.push(feature); }
60496           }
60497           for (var i$1 in borders.features) {
60498             var feature$1 = borders.features[i$1];
60499             feature$1.properties.groups.sort(function(groupID1, groupID2) {
60500               return (
60501                 levels.indexOf(featuresByCode[groupID1].properties.level) -
60502                 levels.indexOf(featuresByCode[groupID2].properties.level)
60503               );
60504             });
60505             loadMembersForGroupsOf(feature$1);
60506           }
60507           var geometryOnlyCollection = {
60508             type: 'RegionFeatureCollection',
60509             features: geometryFeatures
60510           };
60511           whichPolygonGetter = whichPolygon_1(geometryOnlyCollection);
60512           function loadGroups(feature) {
60513             var props = feature.properties;
60514             if (!props.groups) {
60515               props.groups = [];
60516             }
60517             if (props.country) {
60518               props.groups.push(props.country);
60519             }
60520             if (props.m49 !== '001') {
60521               props.groups.push('001');
60522             }
60523           }
60524           function loadM49(feature) {
60525             var props = feature.properties;
60526             if (!props.m49 && props.iso1N3) {
60527               props.m49 = props.iso1N3;
60528             }
60529           }
60530           function loadIsoStatus(feature) {
60531             var props = feature.properties;
60532             if (!props.isoStatus && props.iso1A2) {
60533               props.isoStatus = 'official';
60534             }
60535           }
60536           function loadLevel(feature) {
60537             var props = feature.properties;
60538             if (props.level) { return; }
60539             if (!props.country) {
60540               props.level = 'country';
60541             } else if (props.isoStatus === 'official') {
60542               props.level = 'territory';
60543             } else {
60544               props.level = 'subterritory';
60545             }
60546           }
60547           function loadRoadSpeedUnit(feature) {
60548             var props = feature.properties;
60549             if (props.roadSpeedUnit === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60550               props.roadSpeedUnit = 'km/h';
60551             }
60552           }
60553           function loadDriveSide(feature) {
60554             var props = feature.properties;
60555             if (props.driveSide === undefined && props.iso1A2 && props.iso1A2 !== 'EU') {
60556               props.driveSide = 'right';
60557             }
60558           }
60559           function loadFlag(feature) {
60560             if (!feature.properties.iso1A2) { return; }
60561             var flag = feature.properties.iso1A2.replace(/./g, function(char) {
60562               return String.fromCodePoint(char.charCodeAt(0) + 127397);
60563             });
60564             feature.properties.emojiFlag = flag;
60565           }
60566           function loadMembersForGroupsOf(feature) {
60567             var featureID = feature.properties.id;
60568             var standardizedGroupIDs = [];
60569             for (var j in feature.properties.groups) {
60570               var groupID = feature.properties.groups[j];
60571               var groupFeature = featuresByCode[groupID];
60572               standardizedGroupIDs.push(groupFeature.properties.id);
60573               if (groupFeature.properties.members) {
60574                 groupFeature.properties.members.push(featureID);
60575               } else {
60576                 groupFeature.properties.members = [featureID];
60577               }
60578             }
60579             feature.properties.groups = standardizedGroupIDs;
60580           }
60581           function cacheFeatureByIDs(feature) {
60582             for (var k in identifierProps) {
60583               var prop = identifierProps[k];
60584               var id = prop && feature.properties[prop];
60585               if (id) {
60586                 id = id.replace(idFilterRegex, '').toUpperCase();
60587                 featuresByCode[id] = feature;
60588               }
60589             }
60590             if (feature.properties.aliases) {
60591               for (var j in feature.properties.aliases) {
60592                 var alias = feature.properties.aliases[j].replace(idFilterRegex, '').toUpperCase();
60593                 featuresByCode[alias] = feature;
60594               }
60595             }
60596           }
60597         }
60598         function locArray(loc) {
60599           if (Array.isArray(loc)) {
60600             return loc;
60601           } else if (loc.coordinates) {
60602             return loc.coordinates;
60603           }
60604           return loc.geometry.coordinates;
60605         }
60606         function smallestFeature(loc) {
60607           var query = locArray(loc);
60608           var featureProperties = whichPolygonGetter(query);
60609           if (!featureProperties) { return null; }
60610           return featuresByCode[featureProperties.id];
60611         }
60612         function countryFeature(loc) {
60613           var feature = smallestFeature(loc);
60614           if (!feature) { return null; }
60615           var countryCode = feature.properties.country || feature.properties.iso1A2;
60616           return featuresByCode[countryCode];
60617         }
60618         function featureForLoc(loc, opts) {
60619           if (opts && opts.level && opts.level !== 'country') {
60620             var features = featuresContaining(loc);
60621             var targetLevel = opts.level;
60622             var targetLevelIndex = levels.indexOf(targetLevel);
60623             if (targetLevelIndex === -1) { return null; }
60624             for (var i in features) {
60625               var feature = features[i];
60626               if (
60627                 feature.properties.level === targetLevel ||
60628                 levels.indexOf(feature.properties.level) > targetLevelIndex
60629               ) {
60630                 return feature;
60631               }
60632             }
60633             return null;
60634           }
60635           return countryFeature(loc);
60636         }
60637         function featureForID(id) {
60638           var stringID;
60639           if (typeof id === 'number') {
60640             stringID = id.toString();
60641             if (stringID.length === 1) {
60642               stringID = '00' + stringID;
60643             } else if (stringID.length === 2) {
60644               stringID = '0' + stringID;
60645             }
60646           } else {
60647             stringID = id.replace(idFilterRegex, '').toUpperCase();
60648           }
60649           return featuresByCode[stringID] || null;
60650         }
60651         function smallestOrMatchingFeature(query) {
60652           if (typeof query === 'object') {
60653             return smallestFeature(query);
60654           }
60655           return featureForID(query);
60656         }
60657         function feature(query, opts) {
60658           if (typeof query === 'object') {
60659             return featureForLoc(query, opts);
60660           }
60661           return featureForID(query);
60662         }
60663         function iso1A2Code(query, opts) {
60664           var match = feature(query, opts);
60665           if (!match) { return null; }
60666           return match.properties.iso1A2 || null;
60667         }
60668         function featuresContaining(query, strict) {
60669           var feature = smallestOrMatchingFeature(query);
60670           if (!feature) { return []; }
60671           var features = [];
60672           if (!strict || typeof query === 'object') {
60673             features.push(feature);
60674           }
60675           var properties = feature.properties;
60676           for (var i in properties.groups) {
60677             var groupID = properties.groups[i];
60678             features.push(featuresByCode[groupID]);
60679           }
60680           return features;
60681         }
60682         function roadSpeedUnit(query) {
60683           var feature = smallestOrMatchingFeature(query);
60684           return (feature && feature.properties.roadSpeedUnit) || null;
60685         }
60686
60687         var _dataDeprecated;
60688         var _nsi;
60689
60690         function validationOutdatedTags() {
60691           var type = 'outdated_tags';
60692           var nsiKeys = ['amenity', 'shop', 'tourism', 'leisure', 'office'];
60693
60694           // A concern here in switching to async data means that `_dataDeprecated`
60695           // and `_nsi` will not be available at first, so the data on early tiles
60696           // may not have tags validated fully.
60697
60698           // initialize deprecated tags array
60699           _mainFileFetcher.get('deprecated')
60700             .then(function (d) { return _dataDeprecated = d; })
60701             .catch(function () { /* ignore */ });
60702
60703           _mainFileFetcher.get('nsi_brands')
60704             .then(function (d) {
60705               _nsi = {
60706                 brands: d.brands,
60707                 matcher: matcher$1(),
60708                 wikidata: {},
60709                 wikipedia: {}
60710               };
60711
60712               // initialize name-suggestion-index matcher
60713               _nsi.matcher.buildMatchIndex(d.brands);
60714
60715               // index all known wikipedia and wikidata tags
60716               Object.keys(d.brands).forEach(function (kvnd) {
60717                 var brand = d.brands[kvnd];
60718                 var wd = brand.tags['brand:wikidata'];
60719                 var wp = brand.tags['brand:wikipedia'];
60720                 if (wd) { _nsi.wikidata[wd] = kvnd; }
60721                 if (wp) { _nsi.wikipedia[wp] = kvnd; }
60722               });
60723
60724               return _nsi;
60725             })
60726             .catch(function () { /* ignore */ });
60727
60728
60729           function oldTagIssues(entity, graph) {
60730             var oldTags = Object.assign({}, entity.tags);  // shallow copy
60731             var preset = _mainPresetIndex.match(entity, graph);
60732             var subtype = 'deprecated_tags';
60733             if (!preset) { return []; }
60734
60735             // upgrade preset..
60736             if (preset.replacement) {
60737               var newPreset = _mainPresetIndex.item(preset.replacement);
60738               graph = actionChangePreset(entity.id, preset, newPreset, true /* skip field defaults */)(graph);
60739               entity = graph.entity(entity.id);
60740               preset = newPreset;
60741             }
60742
60743             // upgrade tags..
60744             if (_dataDeprecated) {
60745               var deprecatedTags = entity.deprecatedTags(_dataDeprecated);
60746               if (deprecatedTags.length) {
60747                 deprecatedTags.forEach(function (tag) {
60748                   graph = actionUpgradeTags(entity.id, tag.old, tag.replace)(graph);
60749                 });
60750                 entity = graph.entity(entity.id);
60751               }
60752             }
60753
60754             // add missing addTags..
60755             var newTags = Object.assign({}, entity.tags);  // shallow copy
60756             if (preset.tags !== preset.addTags) {
60757               Object.keys(preset.addTags).forEach(function (k) {
60758                 if (!newTags[k]) {
60759                   if (preset.addTags[k] === '*') {
60760                     newTags[k] = 'yes';
60761                   } else {
60762                     newTags[k] = preset.addTags[k];
60763                   }
60764                 }
60765               });
60766             }
60767
60768             if (_nsi) {
60769               // Do `wikidata` or `wikipedia` identify this entity as a brand?  #6416
60770               // If so, these tags can be swapped to `brand:wikidata`/`brand:wikipedia`
60771               var isBrand;
60772               if (newTags.wikidata) {                 // try matching `wikidata`
60773                 isBrand = _nsi.wikidata[newTags.wikidata];
60774               }
60775               if (!isBrand && newTags.wikipedia) {    // fallback to `wikipedia`
60776                 isBrand = _nsi.wikipedia[newTags.wikipedia];
60777               }
60778               if (isBrand && !newTags.office) {       // but avoid doing this for corporate offices
60779                 if (newTags.wikidata) {
60780                   newTags['brand:wikidata'] = newTags.wikidata;
60781                   delete newTags.wikidata;
60782                 }
60783                 if (newTags.wikipedia) {
60784                   newTags['brand:wikipedia'] = newTags.wikipedia;
60785                   delete newTags.wikipedia;
60786                 }
60787                 // I considered setting `name` and other tags here, but they aren't unique per wikidata
60788                 // (Q2759586 -> in USA "Papa John's", in Russia "Папа Джонс")
60789                 // So users will really need to use a preset or assign `name` themselves.
60790               }
60791
60792               // try key/value|name match against name-suggestion-index
60793               if (newTags.name) {
60794                 for (var i = 0; i < nsiKeys.length; i++) {
60795                   var k = nsiKeys[i];
60796                   if (!newTags[k]) { continue; }
60797
60798                   var center = entity.extent(graph).center();
60799                   var countryCode = iso1A2Code(center);
60800                   var match = _nsi.matcher.matchKVN(k, newTags[k], newTags.name, countryCode && countryCode.toLowerCase());
60801                   if (!match) { continue; }
60802
60803                   // for now skip ambiguous matches (like Target~(USA) vs Target~(Australia))
60804                   if (match.d) { continue; }
60805
60806                   var brand = _nsi.brands[match.kvnd];
60807                   if (brand && brand.tags['brand:wikidata'] &&
60808                     brand.tags['brand:wikidata'] !== entity.tags['not:brand:wikidata']) {
60809                     subtype = 'noncanonical_brand';
60810
60811                     var keepTags = ['takeaway'].reduce(function (acc, k) {
60812                       if (newTags[k]) {
60813                         acc[k] = newTags[k];
60814                       }
60815                       return acc;
60816                     }, {});
60817
60818                     nsiKeys.forEach(function (k) { return delete newTags[k]; });
60819                     Object.assign(newTags, brand.tags, keepTags);
60820                     break;
60821                   }
60822                 }
60823               }
60824             }
60825
60826             // determine diff
60827             var tagDiff = utilTagDiff(oldTags, newTags);
60828             if (!tagDiff.length) { return []; }
60829
60830             var isOnlyAddingTags = tagDiff.every(function (d) { return d.type === '+'; });
60831
60832             var prefix = '';
60833             if (subtype === 'noncanonical_brand') {
60834               prefix = 'noncanonical_brand.';
60835             } else if (subtype === 'deprecated_tags' && isOnlyAddingTags) {
60836               subtype = 'incomplete_tags';
60837               prefix = 'incomplete.';
60838             }
60839
60840             // don't allow autofixing brand tags
60841             var autoArgs = subtype !== 'noncanonical_brand' ? [doUpgrade, _t('issues.fix.upgrade_tags.annotation')] : null;
60842
60843             return [new validationIssue({
60844               type: type,
60845               subtype: subtype,
60846               severity: 'warning',
60847               message: showMessage,
60848               reference: showReference,
60849               entityIds: [entity.id],
60850               hash: JSON.stringify(tagDiff),
60851               dynamicFixes: function () {
60852                 return [
60853                   new validationIssueFix({
60854                     autoArgs: autoArgs,
60855                     title: _t('issues.fix.upgrade_tags.title'),
60856                     onClick: function (context) {
60857                       context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
60858                     }
60859                   })
60860                 ];
60861               }
60862             })];
60863
60864
60865             function doUpgrade(graph) {
60866               var currEntity = graph.hasEntity(entity.id);
60867               if (!currEntity) { return graph; }
60868
60869               var newTags = Object.assign({}, currEntity.tags);  // shallow copy
60870               tagDiff.forEach(function (diff) {
60871                 if (diff.type === '-') {
60872                   delete newTags[diff.key];
60873                 } else if (diff.type === '+') {
60874                   newTags[diff.key] = diff.newVal;
60875                 }
60876               });
60877
60878               return actionChangeTags(currEntity.id, newTags)(graph);
60879             }
60880
60881
60882             function showMessage(context) {
60883               var currEntity = context.hasEntity(entity.id);
60884               if (!currEntity) { return ''; }
60885
60886               var messageID = "issues.outdated_tags." + prefix + "message";
60887               if (subtype === 'noncanonical_brand' && isOnlyAddingTags) {
60888                 messageID += '_incomplete';
60889               }
60890               return _t(messageID, { feature: utilDisplayLabel(currEntity, context.graph()) });
60891             }
60892
60893
60894             function showReference(selection) {
60895               var enter = selection.selectAll('.issue-reference')
60896                 .data([0])
60897                 .enter();
60898
60899               enter
60900                 .append('div')
60901                 .attr('class', 'issue-reference')
60902                 .text(_t(("issues.outdated_tags." + prefix + "reference")));
60903
60904               enter
60905                 .append('strong')
60906                 .text(_t('issues.suggested'));
60907
60908               enter
60909                 .append('table')
60910                 .attr('class', 'tagDiff-table')
60911                 .selectAll('.tagDiff-row')
60912                 .data(tagDiff)
60913                 .enter()
60914                 .append('tr')
60915                 .attr('class', 'tagDiff-row')
60916                 .append('td')
60917                 .attr('class', function (d) {
60918                   var klass = d.type === '+' ? 'add' : 'remove';
60919                   return ("tagDiff-cell tagDiff-cell-" + klass);
60920                 })
60921                 .text(function (d) { return d.display; });
60922             }
60923           }
60924
60925
60926           function oldMultipolygonIssues(entity, graph) {
60927             var multipolygon, outerWay;
60928             if (entity.type === 'relation') {
60929               outerWay = osmOldMultipolygonOuterMemberOfRelation(entity, graph);
60930               multipolygon = entity;
60931             } else if (entity.type === 'way') {
60932               multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
60933               outerWay = entity;
60934             } else {
60935               return [];
60936             }
60937
60938             if (!multipolygon || !outerWay) { return []; }
60939
60940             return [new validationIssue({
60941               type: type,
60942               subtype: 'old_multipolygon',
60943               severity: 'warning',
60944               message: showMessage,
60945               reference: showReference,
60946               entityIds: [outerWay.id, multipolygon.id],
60947               dynamicFixes: function () {
60948                 return [
60949                   new validationIssueFix({
60950                     autoArgs: [doUpgrade, _t('issues.fix.move_tags.annotation')],
60951                     title: _t('issues.fix.move_tags.title'),
60952                     onClick: function (context) {
60953                       context.perform(doUpgrade, _t('issues.fix.move_tags.annotation'));
60954                     }
60955                   })
60956                 ];
60957               }
60958             })];
60959
60960
60961             function doUpgrade(graph) {
60962               var currMultipolygon = graph.hasEntity(multipolygon.id);
60963               var currOuterWay = graph.hasEntity(outerWay.id);
60964               if (!currMultipolygon || !currOuterWay) { return graph; }
60965
60966               currMultipolygon = currMultipolygon.mergeTags(currOuterWay.tags);
60967               graph = graph.replace(currMultipolygon);
60968               return actionChangeTags(currOuterWay.id, {})(graph);
60969             }
60970
60971
60972             function showMessage(context) {
60973               var currMultipolygon = context.hasEntity(multipolygon.id);
60974               if (!currMultipolygon) { return ''; }
60975
60976               return _t('issues.old_multipolygon.message',
60977                   { multipolygon: utilDisplayLabel(currMultipolygon, context.graph()) }
60978               );
60979             }
60980
60981
60982             function showReference(selection) {
60983               selection.selectAll('.issue-reference')
60984                 .data([0])
60985                 .enter()
60986                 .append('div')
60987                 .attr('class', 'issue-reference')
60988                 .text(_t('issues.old_multipolygon.reference'));
60989             }
60990           }
60991
60992
60993           var validation = function checkOutdatedTags(entity, graph) {
60994             var issues = oldMultipolygonIssues(entity, graph);
60995             if (!issues.length) { issues = oldTagIssues(entity, graph); }
60996             return issues;
60997           };
60998
60999
61000           validation.type = type;
61001
61002           return validation;
61003         }
61004
61005         function validationPrivateData() {
61006             var type = 'private_data';
61007
61008             // assume that some buildings are private
61009             var privateBuildingValues = {
61010                 detached: true,
61011                 farm: true,
61012                 house: true,
61013                 houseboat: true,
61014                 residential: true,
61015                 semidetached_house: true,
61016                 static_caravan: true
61017             };
61018
61019             // but they might be public if they have one of these other tags
61020             var publicKeys = {
61021                 amenity: true,
61022                 craft: true,
61023                 historic: true,
61024                 leisure: true,
61025                 office: true,
61026                 shop: true,
61027                 tourism: true
61028             };
61029
61030             // these tags may contain personally identifying info
61031             var personalTags = {
61032                 'contact:email': true,
61033                 'contact:fax': true,
61034                 'contact:phone': true,
61035                 email: true,
61036                 fax: true,
61037                 phone: true
61038             };
61039
61040
61041             var validation = function checkPrivateData(entity) {
61042                 var tags = entity.tags;
61043                 if (!tags.building || !privateBuildingValues[tags.building]) { return []; }
61044
61045                 var keepTags = {};
61046                 for (var k in tags) {
61047                     if (publicKeys[k]) { return []; }  // probably a public feature
61048                     if (!personalTags[k]) {
61049                         keepTags[k] = tags[k];
61050                     }
61051                 }
61052
61053                 var tagDiff = utilTagDiff(tags, keepTags);
61054                 if (!tagDiff.length) { return []; }
61055
61056                 var fixID = tagDiff.length === 1 ? 'remove_tag' : 'remove_tags';
61057
61058                 return [new validationIssue({
61059                     type: type,
61060                     severity: 'warning',
61061                     message: showMessage,
61062                     reference: showReference,
61063                     entityIds: [entity.id],
61064                     dynamicFixes: function() {
61065                         return [
61066                             new validationIssueFix({
61067                                 icon: 'iD-operation-delete',
61068                                 title: _t('issues.fix.' + fixID + '.title'),
61069                                 onClick: function(context) {
61070                                     context.perform(doUpgrade, _t('issues.fix.upgrade_tags.annotation'));
61071                                 }
61072                             })
61073                         ];
61074                     }
61075                 })];
61076
61077
61078                 function doUpgrade(graph) {
61079                     var currEntity = graph.hasEntity(entity.id);
61080                     if (!currEntity) { return graph; }
61081
61082                     var newTags = Object.assign({}, currEntity.tags);  // shallow copy
61083                     tagDiff.forEach(function(diff) {
61084                         if (diff.type === '-') {
61085                             delete newTags[diff.key];
61086                         } else if (diff.type === '+') {
61087                             newTags[diff.key] = diff.newVal;
61088                         }
61089                     });
61090
61091                     return actionChangeTags(currEntity.id, newTags)(graph);
61092                 }
61093
61094
61095                 function showMessage(context) {
61096                     var currEntity = context.hasEntity(this.entityIds[0]);
61097                     if (!currEntity) { return ''; }
61098
61099                     return _t('issues.private_data.contact.message',
61100                         { feature: utilDisplayLabel(currEntity, context.graph()) }
61101                     );
61102                 }
61103
61104
61105                 function showReference(selection) {
61106                     var enter = selection.selectAll('.issue-reference')
61107                         .data([0])
61108                         .enter();
61109
61110                     enter
61111                         .append('div')
61112                         .attr('class', 'issue-reference')
61113                         .text(_t('issues.private_data.reference'));
61114
61115                     enter
61116                         .append('strong')
61117                         .text(_t('issues.suggested'));
61118
61119                     enter
61120                         .append('table')
61121                         .attr('class', 'tagDiff-table')
61122                         .selectAll('.tagDiff-row')
61123                         .data(tagDiff)
61124                         .enter()
61125                         .append('tr')
61126                         .attr('class', 'tagDiff-row')
61127                         .append('td')
61128                         .attr('class', function(d) {
61129                             var klass = d.type === '+' ? 'add' : 'remove';
61130                             return 'tagDiff-cell tagDiff-cell-' + klass;
61131                         })
61132                         .text(function(d) { return d.display; });
61133                 }
61134             };
61135
61136
61137             validation.type = type;
61138
61139             return validation;
61140         }
61141
61142         var _discardNameRegexes = [];
61143
61144         function validationSuspiciousName() {
61145           var type = 'suspicious_name';
61146           var keysToTestForGenericValues = [
61147             'aerialway', 'aeroway', 'amenity', 'building', 'craft', 'highway',
61148             'leisure', 'railway', 'man_made', 'office', 'shop', 'tourism', 'waterway'
61149           ];
61150
61151           // A concern here in switching to async data means that `_nsiFilters` will not
61152           // be available at first, so the data on early tiles may not have tags validated fully.
61153
61154           _mainFileFetcher.get('nsi_filters')
61155             .then(function (filters) {
61156               // known list of generic names (e.g. "bar")
61157               _discardNameRegexes = filters.discardNames
61158                 .map(function (discardName) { return new RegExp(discardName, 'i'); });
61159             })
61160             .catch(function () { /* ignore */ });
61161
61162
61163           function isDiscardedSuggestionName(lowercaseName) {
61164             return _discardNameRegexes.some(function (regex) { return regex.test(lowercaseName); });
61165           }
61166
61167           // test if the name is just the key or tag value (e.g. "park")
61168           function nameMatchesRawTag(lowercaseName, tags) {
61169             for (var i = 0; i < keysToTestForGenericValues.length; i++) {
61170               var key = keysToTestForGenericValues[i];
61171               var val = tags[key];
61172               if (val) {
61173                 val = val.toLowerCase();
61174                 if (key === lowercaseName ||
61175                   val === lowercaseName ||
61176                   key.replace(/\_/g, ' ') === lowercaseName ||
61177                   val.replace(/\_/g, ' ') === lowercaseName) {
61178                   return true;
61179                 }
61180               }
61181             }
61182             return false;
61183           }
61184
61185           function isGenericName(name, tags) {
61186             name = name.toLowerCase();
61187             return nameMatchesRawTag(name, tags) || isDiscardedSuggestionName(name);
61188           }
61189
61190           function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
61191             return new validationIssue({
61192               type: type,
61193               subtype: 'generic_name',
61194               severity: 'warning',
61195               message: function(context) {
61196                 var entity = context.hasEntity(this.entityIds[0]);
61197                 if (!entity) { return ''; }
61198                 var preset = _mainPresetIndex.match(entity, context.graph());
61199                 var langName = langCode && _mainLocalizer.languageName(langCode);
61200                 return _t('issues.generic_name.message' + (langName ? '_language' : ''),
61201                   { feature: preset.name(), name: genericName, language: langName }
61202                 );
61203               },
61204               reference: showReference,
61205               entityIds: [entityId],
61206               hash: nameKey + '=' + genericName,
61207               dynamicFixes: function() {
61208                 return [
61209                   new validationIssueFix({
61210                     icon: 'iD-operation-delete',
61211                     title: _t('issues.fix.remove_the_name.title'),
61212                     onClick: function(context) {
61213                       var entityId = this.issue.entityIds[0];
61214                       var entity = context.entity(entityId);
61215                       var tags = Object.assign({}, entity.tags);   // shallow copy
61216                       delete tags[nameKey];
61217                       context.perform(
61218                         actionChangeTags(entityId, tags), _t('issues.fix.remove_generic_name.annotation')
61219                       );
61220                     }
61221                   })
61222                 ];
61223               }
61224             });
61225
61226             function showReference(selection) {
61227               selection.selectAll('.issue-reference')
61228                 .data([0])
61229                 .enter()
61230                 .append('div')
61231                 .attr('class', 'issue-reference')
61232                 .text(_t('issues.generic_name.reference'));
61233             }
61234           }
61235
61236           function makeIncorrectNameIssue(entityId, nameKey, incorrectName, langCode) {
61237             return new validationIssue({
61238               type: type,
61239               subtype: 'not_name',
61240               severity: 'warning',
61241               message: function(context) {
61242                 var entity = context.hasEntity(this.entityIds[0]);
61243                 if (!entity) { return ''; }
61244                 var preset = _mainPresetIndex.match(entity, context.graph());
61245                 var langName = langCode && _mainLocalizer.languageName(langCode);
61246                 return _t('issues.incorrect_name.message' + (langName ? '_language' : ''),
61247                   { feature: preset.name(), name: incorrectName, language: langName }
61248                 );
61249               },
61250               reference: showReference,
61251               entityIds: [entityId],
61252               hash: nameKey + '=' + incorrectName,
61253               dynamicFixes: function() {
61254                 return [
61255                   new validationIssueFix({
61256                     icon: 'iD-operation-delete',
61257                     title: _t('issues.fix.remove_the_name.title'),
61258                     onClick: function(context) {
61259                       var entityId = this.issue.entityIds[0];
61260                       var entity = context.entity(entityId);
61261                       var tags = Object.assign({}, entity.tags);   // shallow copy
61262                       delete tags[nameKey];
61263                       context.perform(
61264                         actionChangeTags(entityId, tags), _t('issues.fix.remove_mistaken_name.annotation')
61265                       );
61266                     }
61267                   })
61268                 ];
61269               }
61270             });
61271
61272             function showReference(selection) {
61273               selection.selectAll('.issue-reference')
61274                 .data([0])
61275                 .enter()
61276                 .append('div')
61277                 .attr('class', 'issue-reference')
61278                 .text(_t('issues.generic_name.reference'));
61279             }
61280           }
61281
61282
61283           var validation = function checkGenericName(entity) {
61284             // a generic name is okay if it's a known brand or entity
61285             if (entity.hasWikidata()) { return []; }
61286
61287             var issues = [];
61288             var notNames = (entity.tags['not:name'] || '').split(';');
61289
61290             for (var key in entity.tags) {
61291               var m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
61292               if (!m) { continue; }
61293
61294               var langCode = m.length >= 2 ? m[1] : null;
61295               var value = entity.tags[key];
61296               if (notNames.length) {
61297                 for (var i in notNames) {
61298                   var notName = notNames[i];
61299                   if (notName && value === notName) {
61300                     issues.push(makeIncorrectNameIssue(entity.id, key, value, langCode));
61301                     continue;
61302                   }
61303                 }
61304               }
61305               if (isGenericName(value, entity.tags)) {
61306                 issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
61307               }
61308             }
61309
61310             return issues;
61311           };
61312
61313
61314           validation.type = type;
61315
61316           return validation;
61317         }
61318
61319         function validationUnsquareWay(context) {
61320             var type = 'unsquare_way';
61321             var DEFAULT_DEG_THRESHOLD = 5;   // see also issues.js
61322
61323             // use looser epsilon for detection to reduce warnings of buildings that are essentially square already
61324             var epsilon = 0.05;
61325             var nodeThreshold = 10;
61326
61327             function isBuilding(entity, graph) {
61328                 if (entity.type !== 'way' || entity.geometry(graph) !== 'area') { return false; }
61329                 return entity.tags.building && entity.tags.building !== 'no';
61330             }
61331
61332
61333             var validation = function checkUnsquareWay(entity, graph) {
61334
61335                 if (!isBuilding(entity, graph)) { return []; }
61336
61337                 // don't flag ways marked as physically unsquare
61338                 if (entity.tags.nonsquare === 'yes') { return []; }
61339
61340                 var isClosed = entity.isClosed();
61341                 if (!isClosed) { return []; }        // this building has bigger problems
61342
61343                 // don't flag ways with lots of nodes since they are likely detail-mapped
61344                 var nodes = graph.childNodes(entity).slice();    // shallow copy
61345                 if (nodes.length > nodeThreshold + 1) { return []; }   // +1 because closing node appears twice
61346
61347                 // ignore if not all nodes are fully downloaded
61348                 var osm = services.osm;
61349                 if (!osm || nodes.some(function(node) { return !osm.isDataLoaded(node.loc); })) { return []; }
61350
61351                 // don't flag connected ways to avoid unresolvable unsquare loops
61352                 var hasConnectedSquarableWays = nodes.some(function(node) {
61353                     return graph.parentWays(node).some(function(way) {
61354                         if (way.id === entity.id) { return false; }
61355                         if (isBuilding(way, graph)) { return true; }
61356                         return graph.parentRelations(way).some(function(parentRelation) {
61357                             return parentRelation.isMultipolygon() &&
61358                                 parentRelation.tags.building &&
61359                                 parentRelation.tags.building !== 'no';
61360                         });
61361                     });
61362                 });
61363                 if (hasConnectedSquarableWays) { return []; }
61364
61365
61366                 // user-configurable square threshold
61367                 var storedDegreeThreshold = corePreferences('validate-square-degrees');
61368                 var degreeThreshold = isNaN(storedDegreeThreshold) ? DEFAULT_DEG_THRESHOLD : parseFloat(storedDegreeThreshold);
61369
61370                 var points = nodes.map(function(node) { return context.projection(node.loc); });
61371                 if (!geoOrthoCanOrthogonalize(points, isClosed, epsilon, degreeThreshold, true)) { return []; }
61372
61373                 var autoArgs;
61374                 // don't allow autosquaring features linked to wikidata
61375                 if (!entity.tags.wikidata) {
61376                     // use same degree threshold as for detection
61377                     var autoAction = actionOrthogonalize(entity.id, context.projection, undefined, degreeThreshold);
61378                     autoAction.transitionable = false;  // when autofixing, do it instantly
61379                     autoArgs = [autoAction, _t('operations.orthogonalize.annotation.feature.single')];
61380                 }
61381
61382                 return [new validationIssue({
61383                     type: type,
61384                     subtype: 'building',
61385                     severity: 'warning',
61386                     message: function(context) {
61387                         var entity = context.hasEntity(this.entityIds[0]);
61388                         return entity ? _t('issues.unsquare_way.message', { feature: utilDisplayLabel(entity, context.graph()) }) : '';
61389                     },
61390                     reference: showReference,
61391                     entityIds: [entity.id],
61392                     hash: JSON.stringify(autoArgs !== undefined) + degreeThreshold,
61393                     dynamicFixes: function() {
61394                         return [
61395                             new validationIssueFix({
61396                                 icon: 'iD-operation-orthogonalize',
61397                                 title: _t('issues.fix.square_feature.title'),
61398                                 autoArgs: autoArgs,
61399                                 onClick: function(context, completionHandler) {
61400                                     var entityId = this.issue.entityIds[0];
61401                                     // use same degree threshold as for detection
61402                                     context.perform(
61403                                         actionOrthogonalize(entityId, context.projection, undefined, degreeThreshold),
61404                                         _t('operations.orthogonalize.annotation.feature.single')
61405                                     );
61406                                     // run after the squaring transition (currently 150ms)
61407                                     window.setTimeout(function() { completionHandler(); }, 175);
61408                                 }
61409                             }) ];
61410                     }
61411                 })];
61412
61413                 function showReference(selection) {
61414                     selection.selectAll('.issue-reference')
61415                         .data([0])
61416                         .enter()
61417                         .append('div')
61418                         .attr('class', 'issue-reference')
61419                         .text(_t('issues.unsquare_way.buildings.reference'));
61420                 }
61421             };
61422
61423             validation.type = type;
61424
61425             return validation;
61426         }
61427
61428         var Validations = /*#__PURE__*/Object.freeze({
61429                 __proto__: null,
61430                 validationAlmostJunction: validationAlmostJunction,
61431                 validationCloseNodes: validationCloseNodes,
61432                 validationCrossingWays: validationCrossingWays,
61433                 validationDisconnectedWay: validationDisconnectedWay,
61434                 validationFormatting: validationFormatting,
61435                 validationHelpRequest: validationHelpRequest,
61436                 validationImpossibleOneway: validationImpossibleOneway,
61437                 validationIncompatibleSource: validationIncompatibleSource,
61438                 validationMaprules: validationMaprules,
61439                 validationMismatchedGeometry: validationMismatchedGeometry,
61440                 validationMissingRole: validationMissingRole,
61441                 validationMissingTag: validationMissingTag,
61442                 validationOutdatedTags: validationOutdatedTags,
61443                 validationPrivateData: validationPrivateData,
61444                 validationSuspiciousName: validationSuspiciousName,
61445                 validationUnsquareWay: validationUnsquareWay
61446         });
61447
61448         function coreValidator(context) {
61449             var dispatch$1 = dispatch('validated', 'focusedIssue');
61450             var validator = utilRebind({}, dispatch$1, 'on');
61451
61452             var _rules = {};
61453             var _disabledRules = {};
61454
61455             var _ignoredIssueIDs = {};          // issue.id -> true
61456             var _baseCache = validationCache(); // issues before any user edits
61457             var _headCache = validationCache(); // issues after all user edits
61458             var _validatedGraph = null;
61459             var _deferred = new Set();
61460
61461             //
61462             // initialize the validator rulesets
61463             //
61464             validator.init = function() {
61465                 Object.values(Validations).forEach(function(validation) {
61466                     if (typeof validation !== 'function') { return; }
61467
61468                     var fn = validation(context);
61469                     var key = fn.type;
61470                     _rules[key] = fn;
61471                 });
61472
61473                 var disabledRules = corePreferences('validate-disabledRules');
61474                 if (disabledRules) {
61475                     disabledRules.split(',')
61476                         .forEach(function(key) { _disabledRules[key] = true; });
61477                 }
61478             };
61479
61480
61481             //
61482             // clear caches, called whenever iD resets after a save
61483             //
61484             validator.reset = function() {
61485                 Array.from(_deferred).forEach(function(handle) {
61486                     window.cancelIdleCallback(handle);
61487                     _deferred.delete(handle);
61488                 });
61489
61490                 // clear caches
61491                 _ignoredIssueIDs = {};
61492                 _baseCache = validationCache();
61493                 _headCache = validationCache();
61494                 _validatedGraph = null;
61495             };
61496
61497             validator.resetIgnoredIssues = function() {
61498                 _ignoredIssueIDs = {};
61499                 // reload UI
61500                 dispatch$1.call('validated');
61501             };
61502
61503
61504             // must update issues when the user changes the unsquare thereshold
61505             validator.reloadUnsquareIssues = function() {
61506
61507                 reloadUnsquareIssues(_headCache, context.graph());
61508                 reloadUnsquareIssues(_baseCache, context.history().base());
61509
61510                 dispatch$1.call('validated');
61511             };
61512
61513             function reloadUnsquareIssues(cache, graph) {
61514
61515                 var checkUnsquareWay = _rules.unsquare_way;
61516                 if (typeof checkUnsquareWay !== 'function') { return; }
61517
61518                 // uncache existing
61519                 cache.uncacheIssuesOfType('unsquare_way');
61520
61521                 var buildings = context.history().tree().intersects(geoExtent([-180,-90],[180, 90]), graph)  // everywhere
61522                     .filter(function(entity) {
61523                         return entity.type === 'way' && entity.tags.building && entity.tags.building !== 'no';
61524                     });
61525
61526                 // rerun for all buildings
61527                 buildings.forEach(function(entity) {
61528                     var detected = checkUnsquareWay(entity, graph);
61529                     if (detected.length !== 1) { return; }
61530                     var issue = detected[0];
61531                     if (!cache.issuesByEntityID[entity.id]) {
61532                         cache.issuesByEntityID[entity.id] = new Set();
61533                     }
61534                     cache.issuesByEntityID[entity.id].add(issue.id);
61535                     cache.issuesByIssueID[issue.id] = issue;
61536                 });
61537             }
61538
61539             // options = {
61540             //     what: 'all',     // 'all' or 'edited'
61541             //     where: 'all',   // 'all' or 'visible'
61542             //     includeIgnored: false   // true, false, or 'only'
61543             //     includeDisabledRules: false   // true, false, or 'only'
61544             // };
61545             validator.getIssues = function(options) {
61546                 var opts = Object.assign({ what: 'all', where: 'all', includeIgnored: false, includeDisabledRules: false }, options);
61547                 var issues = Object.values(_headCache.issuesByIssueID);
61548                 var view = context.map().extent();
61549
61550                 return issues.filter(function(issue) {
61551                     if (!issue) { return false; }
61552                     if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61553                     if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61554
61555                     if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61556                     if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61557
61558                     // Sanity check:  This issue may be for an entity that not longer exists.
61559                     // If we detect this, uncache and return false so it is not included..
61560                     var entityIds = issue.entityIds || [];
61561                     for (var i = 0; i < entityIds.length; i++) {
61562                         var entityId = entityIds[i];
61563                         if (!context.hasEntity(entityId)) {
61564                             delete _headCache.issuesByEntityID[entityId];
61565                             delete _headCache.issuesByIssueID[issue.id];
61566                             return false;
61567                         }
61568                     }
61569
61570                     if (opts.what === 'edited' && _baseCache.issuesByIssueID[issue.id]) { return false; }
61571
61572                     if (opts.where === 'visible') {
61573                         var extent = issue.extent(context.graph());
61574                         if (!view.intersects(extent)) { return false; }
61575                     }
61576
61577                     return true;
61578                 });
61579             };
61580
61581             validator.getResolvedIssues = function() {
61582                 var baseIssues = Object.values(_baseCache.issuesByIssueID);
61583                 return baseIssues.filter(function(issue) {
61584                     return !_headCache.issuesByIssueID[issue.id];
61585                 });
61586             };
61587
61588             validator.focusIssue = function(issue) {
61589                 var extent = issue.extent(context.graph());
61590
61591                 if (extent) {
61592                     var setZoom = Math.max(context.map().zoom(), 19);
61593                     context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
61594
61595                     // select the first entity
61596                     if (issue.entityIds && issue.entityIds.length) {
61597                         window.setTimeout(function() {
61598                             var ids = issue.entityIds;
61599                             context.enter(modeSelect(context, [ids[0]]));
61600                             dispatch$1.call('focusedIssue', this, issue);
61601                         }, 250);  // after ease
61602                     }
61603                 }
61604             };
61605
61606
61607             validator.getIssuesBySeverity = function(options) {
61608                 var groups = utilArrayGroupBy(validator.getIssues(options), 'severity');
61609                 groups.error = groups.error || [];
61610                 groups.warning = groups.warning || [];
61611                 return groups;
61612             };
61613
61614             // show some issue types in a particular order
61615             var orderedIssueTypes = [
61616                 // flag missing data first
61617                 'missing_tag', 'missing_role',
61618                 // then flag identity issues
61619                 'outdated_tags', 'mismatched_geometry',
61620                 // flag geometry issues where fixing them might solve connectivity issues
61621                 'crossing_ways', 'almost_junction',
61622                 // then flag connectivity issues
61623                 'disconnected_way', 'impossible_oneway'
61624             ];
61625
61626             // returns the issues that the given entity IDs have in common, matching the given options
61627             validator.getSharedEntityIssues = function(entityIDs, options) {
61628                 var cache = _headCache;
61629
61630                 // gather the issues that are common to all the entities
61631                 var issueIDs = entityIDs.reduce(function(acc, entityID) {
61632                     var entityIssueIDs = cache.issuesByEntityID[entityID] || new Set();
61633                     if (!acc) {
61634                         return new Set(entityIssueIDs);
61635                     }
61636                     return new Set([].concat( acc ).filter(function(elem) {
61637                         return entityIssueIDs.has(elem);
61638                     }));
61639                 }, null) || [];
61640
61641                 var opts = options || {};
61642
61643                 return Array.from(issueIDs)
61644                     .map(function(id) { return cache.issuesByIssueID[id]; })
61645                     .filter(function(issue) {
61646                         if (!issue) { return false; }
61647                         if (opts.includeDisabledRules === 'only' && !_disabledRules[issue.type]) { return false; }
61648                         if (!opts.includeDisabledRules && _disabledRules[issue.type]) { return false; }
61649
61650                         if (opts.includeIgnored === 'only' && !_ignoredIssueIDs[issue.id]) { return false; }
61651                         if (!opts.includeIgnored && _ignoredIssueIDs[issue.id]) { return false; }
61652
61653                         return true;
61654                     }).sort(function(issue1, issue2) {
61655                         if (issue1.type === issue2.type) {
61656                             // issues of the same type, sort deterministically
61657                             return issue1.id < issue2.id ? -1 : 1;
61658                         }
61659                         var index1 = orderedIssueTypes.indexOf(issue1.type);
61660                         var index2 = orderedIssueTypes.indexOf(issue2.type);
61661                         if (index1 !== -1 && index2 !== -1) {
61662                             // both issue types have explicit sort orders
61663                             return index1 - index2;
61664                         } else if (index1 === -1 && index2 === -1) {
61665                             // neither issue type has an explicit sort order, sort by type
61666                             return issue1.type < issue2.type ? -1 : 1;
61667                         } else {
61668                             // order explicit types before everything else
61669                             return index1 !== -1 ? -1 : 1;
61670                         }
61671                     });
61672             };
61673
61674
61675             validator.getEntityIssues = function(entityID, options) {
61676                 return validator.getSharedEntityIssues([entityID], options);
61677             };
61678
61679
61680             validator.getRuleKeys = function() {
61681                 return Object.keys(_rules);
61682             };
61683
61684
61685             validator.isRuleEnabled = function(key) {
61686                 return !_disabledRules[key];
61687             };
61688
61689
61690             validator.toggleRule = function(key) {
61691                 if (_disabledRules[key]) {
61692                     delete _disabledRules[key];
61693                 } else {
61694                     _disabledRules[key] = true;
61695                 }
61696
61697                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61698                 validator.validate();
61699             };
61700
61701
61702             validator.disableRules = function(keys) {
61703                 _disabledRules = {};
61704                 keys.forEach(function(k) {
61705                     _disabledRules[k] = true;
61706                 });
61707
61708                 corePreferences('validate-disabledRules', Object.keys(_disabledRules).join(','));
61709                 validator.validate();
61710             };
61711
61712
61713             validator.ignoreIssue = function(id) {
61714                 _ignoredIssueIDs[id] = true;
61715             };
61716
61717
61718             //
61719             // Run validation on a single entity for the given graph
61720             //
61721             function validateEntity(entity, graph) {
61722                 var entityIssues = [];
61723
61724                 // runs validation and appends resulting issues
61725                 function runValidation(key) {
61726
61727                     var fn = _rules[key];
61728                     if (typeof fn !== 'function') {
61729                         console.error('no such validation rule = ' + key);  // eslint-disable-line no-console
61730                         return;
61731                     }
61732
61733                     var detected = fn(entity, graph);
61734                     entityIssues = entityIssues.concat(detected);
61735                 }
61736
61737                 // run all rules
61738                 Object.keys(_rules).forEach(runValidation);
61739
61740                 return entityIssues;
61741             }
61742
61743             function entityIDsToValidate(entityIDs, graph) {
61744                 var processedIDs = new Set();
61745                 return entityIDs.reduce(function(acc, entityID) {
61746                     // keep redundancy check separate from `acc` because an `entityID`
61747                     // could have been added to `acc` as a related entity through an earlier pass
61748                     if (processedIDs.has(entityID)) { return acc; }
61749                     processedIDs.add(entityID);
61750
61751                     var entity = graph.hasEntity(entityID);
61752                     if (!entity) { return acc; }
61753
61754                     acc.add(entityID);
61755
61756                     var checkParentRels = [entity];
61757
61758                     if (entity.type === 'node') {
61759                         graph.parentWays(entity).forEach(function(parentWay) {
61760                             acc.add(parentWay.id); // include parent ways
61761                             checkParentRels.push(parentWay);
61762                         });
61763                     } else if (entity.type === 'relation') {
61764                         entity.members.forEach(function(member) {
61765                             acc.add(member.id); // include members
61766                         });
61767                     } else if (entity.type === 'way') {
61768                         entity.nodes.forEach(function(nodeID) {
61769                             acc.add(nodeID); // include child nodes
61770                             graph._parentWays[nodeID].forEach(function(wayID) {
61771                                 acc.add(wayID); // include connected ways
61772                             });
61773                         });
61774                     }
61775
61776                     checkParentRels.forEach(function(entity) {   // include parent relations
61777                         if (entity.type !== 'relation') {        // but not super-relations
61778                             graph.parentRelations(entity).forEach(function(parentRelation) {
61779                                 acc.add(parentRelation.id);
61780                             });
61781                         }
61782                     });
61783
61784                     return acc;
61785
61786                 }, new Set());
61787             }
61788
61789             //
61790             // Run validation for several entities, supplied `entityIDs`,
61791             // against `graph` for the given `cache`
61792             //
61793             function validateEntities(entityIDs, graph, cache) {
61794
61795                 // clear caches for existing issues related to these entities
61796                 entityIDs.forEach(cache.uncacheEntityID);
61797
61798                 // detect new issues and update caches
61799                 entityIDs.forEach(function(entityID) {
61800                     var entity = graph.hasEntity(entityID);
61801                     // don't validate deleted entities
61802                     if (!entity) { return; }
61803
61804                     var issues = validateEntity(entity, graph);
61805                     cache.cacheIssues(issues);
61806                 });
61807             }
61808
61809
61810             //
61811             // Validates anything that has changed since the last time it was run.
61812             // Also updates the "validatedGraph" to be the current graph
61813             // and dispatches a `validated` event when finished.
61814             //
61815             validator.validate = function() {
61816
61817                 var currGraph = context.graph();
61818                 _validatedGraph = _validatedGraph || context.history().base();
61819                 if (currGraph === _validatedGraph) {
61820                     dispatch$1.call('validated');
61821                     return;
61822                 }
61823                 var oldGraph = _validatedGraph;
61824                 var difference = coreDifference(oldGraph, currGraph);
61825                 _validatedGraph = currGraph;
61826
61827                 var createdAndModifiedEntityIDs = difference.extantIDs(true);   // created/modified (true = w/relation members)
61828                 var entityIDsToCheck = entityIDsToValidate(createdAndModifiedEntityIDs, currGraph);
61829
61830                 // check modified and deleted entities against the old graph in order to update their related entities
61831                 // (e.g. deleting the only highway connected to a road should create a disconnected highway issue)
61832                 var modifiedAndDeletedEntityIDs = difference.deleted().concat(difference.modified())
61833                     .map(function(entity) { return entity.id; });
61834                 var entityIDsToCheckForOldGraph = entityIDsToValidate(modifiedAndDeletedEntityIDs, oldGraph);
61835
61836                 // concat the sets
61837                 entityIDsToCheckForOldGraph.forEach(entityIDsToCheck.add, entityIDsToCheck);
61838
61839                 validateEntities(entityIDsToCheck, context.graph(), _headCache);
61840
61841                 dispatch$1.call('validated');
61842             };
61843
61844
61845             // WHEN TO RUN VALIDATION:
61846             // When graph changes:
61847             context.history()
61848                 .on('restore.validator', validator.validate)   // restore saved history
61849                 .on('undone.validator', validator.validate)    // undo
61850                 .on('redone.validator', validator.validate);   // redo
61851                 // but not on 'change' (e.g. while drawing)
61852
61853             // When user chages editing modes:
61854             context
61855                 .on('exit.validator', validator.validate);
61856
61857             // When merging fetched data:
61858             context.history()
61859                 .on('merge.validator', function(entities) {
61860                     if (!entities) { return; }
61861                     var handle = window.requestIdleCallback(function() {
61862                         var entityIDs = entities.map(function(entity) { return entity.id; });
61863                         var headGraph = context.graph();
61864                         validateEntities(entityIDsToValidate(entityIDs, headGraph), headGraph, _headCache);
61865
61866                         var baseGraph = context.history().base();
61867                         validateEntities(entityIDsToValidate(entityIDs, baseGraph), baseGraph, _baseCache);
61868
61869                         dispatch$1.call('validated');
61870                     });
61871                     _deferred.add(handle);
61872                 });
61873
61874
61875             return validator;
61876         }
61877
61878
61879         function validationCache() {
61880
61881             var cache = {
61882                 issuesByIssueID: {},  // issue.id -> issue
61883                 issuesByEntityID: {} // entity.id -> set(issue.id)
61884             };
61885
61886             cache.cacheIssues = function(issues) {
61887                 issues.forEach(function(issue) {
61888                     var entityIds = issue.entityIds || [];
61889                     entityIds.forEach(function(entityId) {
61890                         if (!cache.issuesByEntityID[entityId]) {
61891                             cache.issuesByEntityID[entityId] = new Set();
61892                         }
61893                         cache.issuesByEntityID[entityId].add(issue.id);
61894                     });
61895                     cache.issuesByIssueID[issue.id] = issue;
61896                 });
61897             };
61898
61899             cache.uncacheIssue = function(issue) {
61900                 // When multiple entities are involved (e.g. crossing_ways),
61901                 // remove this issue from the other entity caches too..
61902                 var entityIds = issue.entityIds || [];
61903                 entityIds.forEach(function(entityId) {
61904                     if (cache.issuesByEntityID[entityId]) {
61905                         cache.issuesByEntityID[entityId].delete(issue.id);
61906                     }
61907                 });
61908                 delete cache.issuesByIssueID[issue.id];
61909             };
61910
61911             cache.uncacheIssues = function(issues) {
61912                 issues.forEach(cache.uncacheIssue);
61913             };
61914
61915             cache.uncacheIssuesOfType = function(type) {
61916                 var issuesOfType = Object.values(cache.issuesByIssueID)
61917                     .filter(function(issue) { return issue.type === type; });
61918                 cache.uncacheIssues(issuesOfType);
61919             };
61920
61921             //
61922             // Remove a single entity and all its related issues from the caches
61923             //
61924             cache.uncacheEntityID = function(entityID) {
61925                 var issueIDs = cache.issuesByEntityID[entityID];
61926                 if (!issueIDs) { return; }
61927
61928                 issueIDs.forEach(function(issueID) {
61929                     var issue = cache.issuesByIssueID[issueID];
61930                     if (issue) {
61931                         cache.uncacheIssue(issue);
61932                     } else {
61933                         delete cache.issuesByIssueID[issueID];
61934                     }
61935                 });
61936
61937                 delete cache.issuesByEntityID[entityID];
61938             };
61939
61940             return cache;
61941         }
61942
61943         function coreUploader(context) {
61944
61945             var dispatch$1 = dispatch(
61946                 // Start and end events are dispatched exactly once each per legitimate outside call to `save`
61947                 'saveStarted', // dispatched as soon as a call to `save` has been deemed legitimate
61948                 'saveEnded',   // dispatched after the result event has been dispatched
61949
61950                 'willAttemptUpload', // dispatched before the actual upload call occurs, if it will
61951                 'progressChanged',
61952
61953                 // Each save results in one of these outcomes:
61954                 'resultNoChanges', // upload wasn't attempted since there were no edits
61955                 'resultErrors',    // upload failed due to errors
61956                 'resultConflicts', // upload failed due to data conflicts
61957                 'resultSuccess'    // upload completed without errors
61958             );
61959
61960             var _isSaving = false;
61961
61962             var _conflicts = [];
61963             var _errors = [];
61964             var _origChanges;
61965
61966             var _discardTags = {};
61967             _mainFileFetcher.get('discarded')
61968                 .then(function(d) { _discardTags = d; })
61969                 .catch(function() { /* ignore */ });
61970
61971             var uploader = utilRebind({}, dispatch$1, 'on');
61972
61973             uploader.isSaving = function() {
61974                 return _isSaving;
61975             };
61976
61977             uploader.save = function(changeset, tryAgain, checkConflicts) {
61978                 // Guard against accidentally entering save code twice - #4641
61979                 if (_isSaving && !tryAgain) {
61980                     return;
61981                 }
61982
61983                 var osm = context.connection();
61984                 if (!osm) { return; }
61985
61986                 // If user somehow got logged out mid-save, try to reauthenticate..
61987                 // This can happen if they were logged in from before, but the tokens are no longer valid.
61988                 if (!osm.authenticated()) {
61989                     osm.authenticate(function(err) {
61990                         if (!err) {
61991                             uploader.save(changeset, tryAgain, checkConflicts);  // continue where we left off..
61992                         }
61993                     });
61994                     return;
61995                 }
61996
61997                 if (!_isSaving) {
61998                     _isSaving = true;
61999                     dispatch$1.call('saveStarted', this);
62000                 }
62001
62002                 var history = context.history();
62003
62004                 _conflicts = [];
62005                 _errors = [];
62006
62007                 // Store original changes, in case user wants to download them as an .osc file
62008                 _origChanges = history.changes(actionDiscardTags(history.difference(), _discardTags));
62009
62010                 // First time, `history.perform` a no-op action.
62011                 // Any conflict resolutions will be done as `history.replace`
62012                 // Remember to pop this later if needed
62013                 if (!tryAgain) {
62014                     history.perform(actionNoop());
62015                 }
62016
62017                 // Attempt a fast upload.. If there are conflicts, re-enter with `checkConflicts = true`
62018                 if (!checkConflicts) {
62019                     upload(changeset);
62020
62021                 // Do the full (slow) conflict check..
62022                 } else {
62023                     performFullConflictCheck(changeset);
62024                 }
62025
62026             };
62027
62028
62029             function performFullConflictCheck(changeset) {
62030
62031                 var osm = context.connection();
62032                 if (!osm) { return; }
62033
62034                 var history = context.history();
62035
62036                 var localGraph = context.graph();
62037                 var remoteGraph = coreGraph(history.base(), true);
62038
62039                 var summary = history.difference().summary();
62040                 var _toCheck = [];
62041                 for (var i = 0; i < summary.length; i++) {
62042                     var item = summary[i];
62043                     if (item.changeType === 'modified') {
62044                         _toCheck.push(item.entity.id);
62045                     }
62046                 }
62047
62048                 var _toLoad = withChildNodes(_toCheck, localGraph);
62049                 var _loaded = {};
62050                 var _toLoadCount = 0;
62051                 var _toLoadTotal = _toLoad.length;
62052
62053                 if (_toCheck.length) {
62054                     dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62055                     _toLoad.forEach(function(id) { _loaded[id] = false; });
62056                     osm.loadMultiple(_toLoad, loaded);
62057                 } else {
62058                     upload(changeset);
62059                 }
62060
62061                 return;
62062
62063                 function withChildNodes(ids, graph) {
62064                     var s = new Set(ids);
62065                     ids.forEach(function(id) {
62066                         var entity = graph.entity(id);
62067                         if (entity.type !== 'way') { return; }
62068
62069                         graph.childNodes(entity).forEach(function(child) {
62070                             if (child.version !== undefined) {
62071                                 s.add(child.id);
62072                             }
62073                         });
62074                     });
62075
62076                     return Array.from(s);
62077                 }
62078
62079
62080                 // Reload modified entities into an alternate graph and check for conflicts..
62081                 function loaded(err, result) {
62082                     if (_errors.length) { return; }
62083
62084                     if (err) {
62085                         _errors.push({
62086                             msg: err.message || err.responseText,
62087                             details: [ _t('save.status_code', { code: err.status }) ]
62088                         });
62089                         didResultInErrors();
62090
62091                     } else {
62092                         var loadMore = [];
62093
62094                         result.data.forEach(function(entity) {
62095                             remoteGraph.replace(entity);
62096                             _loaded[entity.id] = true;
62097                             _toLoad = _toLoad.filter(function(val) { return val !== entity.id; });
62098
62099                             if (!entity.visible) { return; }
62100
62101                             // Because loadMultiple doesn't download /full like loadEntity,
62102                             // need to also load children that aren't already being checked..
62103                             var i, id;
62104                             if (entity.type === 'way') {
62105                                 for (i = 0; i < entity.nodes.length; i++) {
62106                                     id = entity.nodes[i];
62107                                     if (_loaded[id] === undefined) {
62108                                         _loaded[id] = false;
62109                                         loadMore.push(id);
62110                                     }
62111                                 }
62112                             } else if (entity.type === 'relation' && entity.isMultipolygon()) {
62113                                 for (i = 0; i < entity.members.length; i++) {
62114                                     id = entity.members[i].id;
62115                                     if (_loaded[id] === undefined) {
62116                                         _loaded[id] = false;
62117                                         loadMore.push(id);
62118                                     }
62119                                 }
62120                             }
62121                         });
62122
62123                         _toLoadCount += result.data.length;
62124                         _toLoadTotal += loadMore.length;
62125                         dispatch$1.call('progressChanged', this, _toLoadCount, _toLoadTotal);
62126
62127                         if (loadMore.length) {
62128                             _toLoad.push.apply(_toLoad, loadMore);
62129                             osm.loadMultiple(loadMore, loaded);
62130                         }
62131
62132                         if (!_toLoad.length) {
62133                             detectConflicts();
62134                             upload(changeset);
62135                         }
62136                     }
62137                 }
62138
62139
62140                 function detectConflicts() {
62141                     function choice(id, text, action) {
62142                         return {
62143                             id: id,
62144                             text: text,
62145                             action: function() {
62146                                 history.replace(action);
62147                             }
62148                         };
62149                     }
62150                     function formatUser(d) {
62151                         return '<a href="' + osm.userURL(d) + '" target="_blank">' + d + '</a>';
62152                     }
62153                     function entityName(entity) {
62154                         return utilDisplayName(entity) || (utilDisplayType(entity.id) + ' ' + entity.id);
62155                     }
62156
62157                     function sameVersions(local, remote) {
62158                         if (local.version !== remote.version) { return false; }
62159
62160                         if (local.type === 'way') {
62161                             var children = utilArrayUnion(local.nodes, remote.nodes);
62162                             for (var i = 0; i < children.length; i++) {
62163                                 var a = localGraph.hasEntity(children[i]);
62164                                 var b = remoteGraph.hasEntity(children[i]);
62165                                 if (a && b && a.version !== b.version) { return false; }
62166                             }
62167                         }
62168
62169                         return true;
62170                     }
62171
62172                     _toCheck.forEach(function(id) {
62173                         var local = localGraph.entity(id);
62174                         var remote = remoteGraph.entity(id);
62175
62176                         if (sameVersions(local, remote)) { return; }
62177
62178                         var merge = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags, formatUser);
62179
62180                         history.replace(merge);
62181
62182                         var mergeConflicts = merge.conflicts();
62183                         if (!mergeConflicts.length) { return; }  // merged safely
62184
62185                         var forceLocal = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_local');
62186                         var forceRemote = actionMergeRemoteChanges(id, localGraph, remoteGraph, _discardTags).withOption('force_remote');
62187                         var keepMine = _t('save.conflict.' + (remote.visible ? 'keep_local' : 'restore'));
62188                         var keepTheirs = _t('save.conflict.' + (remote.visible ? 'keep_remote' : 'delete'));
62189
62190                         _conflicts.push({
62191                             id: id,
62192                             name: entityName(local),
62193                             details: mergeConflicts,
62194                             chosen: 1,
62195                             choices: [
62196                                 choice(id, keepMine, forceLocal),
62197                                 choice(id, keepTheirs, forceRemote)
62198                             ]
62199                         });
62200                     });
62201                 }
62202             }
62203
62204
62205             function upload(changeset) {
62206                 var osm = context.connection();
62207                 if (!osm) {
62208                     _errors.push({ msg: 'No OSM Service' });
62209                 }
62210
62211                 if (_conflicts.length) {
62212                     didResultInConflicts(changeset);
62213
62214                 } else if (_errors.length) {
62215                     didResultInErrors();
62216
62217                 } else {
62218                     var history = context.history();
62219                     var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
62220                     if (changes.modified.length || changes.created.length || changes.deleted.length) {
62221
62222                         dispatch$1.call('willAttemptUpload', this);
62223
62224                         osm.putChangeset(changeset, changes, uploadCallback);
62225
62226                     } else {
62227                         // changes were insignificant or reverted by user
62228                         didResultInNoChanges();
62229                     }
62230                 }
62231             }
62232
62233
62234             function uploadCallback(err, changeset) {
62235                 if (err) {
62236                     if (err.status === 409) {  // 409 Conflict
62237                         uploader.save(changeset, true, true);  // tryAgain = true, checkConflicts = true
62238                     } else {
62239                         _errors.push({
62240                             msg: err.message || err.responseText,
62241                             details: [ _t('save.status_code', { code: err.status }) ]
62242                         });
62243                         didResultInErrors();
62244                     }
62245
62246                 } else {
62247                     didResultInSuccess(changeset);
62248                 }
62249             }
62250
62251             function didResultInNoChanges() {
62252
62253                 dispatch$1.call('resultNoChanges', this);
62254
62255                 endSave();
62256
62257                 context.flush(); // reset iD
62258             }
62259
62260             function didResultInErrors() {
62261
62262                 context.history().pop();
62263
62264                 dispatch$1.call('resultErrors', this, _errors);
62265
62266                 endSave();
62267             }
62268
62269
62270             function didResultInConflicts(changeset) {
62271
62272                 _conflicts.sort(function(a, b) { return b.id.localeCompare(a.id); });
62273
62274                 dispatch$1.call('resultConflicts', this, changeset, _conflicts, _origChanges);
62275
62276                 endSave();
62277             }
62278
62279
62280             function didResultInSuccess(changeset) {
62281
62282                 // delete the edit stack cached to local storage
62283                 context.history().clearSaved();
62284
62285                 dispatch$1.call('resultSuccess', this, changeset);
62286
62287                 // Add delay to allow for postgres replication #1646 #2678
62288                 window.setTimeout(function() {
62289
62290                     endSave();
62291
62292                     context.flush(); // reset iD
62293                 }, 2500);
62294             }
62295
62296
62297             function endSave() {
62298                 _isSaving = false;
62299
62300                 dispatch$1.call('saveEnded', this);
62301             }
62302
62303
62304             uploader.cancelConflictResolution = function() {
62305                 context.history().pop();
62306             };
62307
62308
62309             uploader.processResolvedConflicts = function(changeset) {
62310                 var history = context.history();
62311
62312                 for (var i = 0; i < _conflicts.length; i++) {
62313                     if (_conflicts[i].chosen === 1) {  // user chose "use theirs"
62314                         var entity = context.hasEntity(_conflicts[i].id);
62315                         if (entity && entity.type === 'way') {
62316                             var children = utilArrayUniq(entity.nodes);
62317                             for (var j = 0; j < children.length; j++) {
62318                                 history.replace(actionRevert(children[j]));
62319                             }
62320                         }
62321                         history.replace(actionRevert(_conflicts[i].id));
62322                     }
62323                 }
62324
62325                 uploader.save(changeset, true, false);  // tryAgain = true, checkConflicts = false
62326             };
62327
62328
62329             uploader.reset = function() {
62330
62331             };
62332
62333
62334             return uploader;
62335         }
62336
62337         var isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62338
62339         // listen for DPI change, e.g. when dragging a browser window from a retina to non-retina screen
62340         window.matchMedia("\n        (-webkit-min-device-pixel-ratio: 2), /* Safari */\n        (min-resolution: 2dppx),             /* standard */\n        (min-resolution: 192dpi)             /* fallback */\n    ").addListener(function() {
62341
62342             isRetina = window.devicePixelRatio && window.devicePixelRatio >= 2;
62343         });
62344
62345
62346         function localeDateString(s) {
62347             if (!s) { return null; }
62348             var options = { day: 'numeric', month: 'short', year: 'numeric' };
62349             var d = new Date(s);
62350             if (isNaN(d.getTime())) { return null; }
62351             return d.toLocaleDateString(_mainLocalizer.localeCode(), options);
62352         }
62353
62354         function vintageRange(vintage) {
62355             var s;
62356             if (vintage.start || vintage.end) {
62357                 s = (vintage.start || '?');
62358                 if (vintage.start !== vintage.end) {
62359                     s += ' - ' + (vintage.end || '?');
62360                 }
62361             }
62362             return s;
62363         }
62364
62365
62366         function rendererBackgroundSource(data) {
62367             var source = Object.assign({}, data);   // shallow copy
62368             var _offset = [0, 0];
62369             var _name = source.name;
62370             var _description = source.description;
62371             var _best = !!source.best;
62372             var _template = source.encrypted ? utilAesDecrypt(source.template) : source.template;
62373
62374             source.tileSize = data.tileSize || 256;
62375             source.zoomExtent = data.zoomExtent || [0, 22];
62376             source.overzoom = data.overzoom !== false;
62377
62378             source.offset = function(val) {
62379                 if (!arguments.length) { return _offset; }
62380                 _offset = val;
62381                 return source;
62382             };
62383
62384
62385             source.nudge = function(val, zoomlevel) {
62386                 _offset[0] += val[0] / Math.pow(2, zoomlevel);
62387                 _offset[1] += val[1] / Math.pow(2, zoomlevel);
62388                 return source;
62389             };
62390
62391
62392             source.name = function() {
62393                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62394                 return _t('imagery.' + id_safe + '.name', { default: _name });
62395             };
62396
62397
62398             source.description = function() {
62399                 var id_safe = source.id.replace(/\./g, '<TX_DOT>');
62400                 return _t('imagery.' + id_safe + '.description', { default: _description });
62401             };
62402
62403
62404             source.best = function() {
62405                 return _best;
62406             };
62407
62408
62409             source.area = function() {
62410                 if (!data.polygon) { return Number.MAX_VALUE; }  // worldwide
62411                 var area = d3_geoArea({ type: 'MultiPolygon', coordinates: [ data.polygon ] });
62412                 return isNaN(area) ? 0 : area;
62413             };
62414
62415
62416             source.imageryUsed = function() {
62417                 return name || source.id;
62418             };
62419
62420
62421             source.template = function(val) {
62422                 if (!arguments.length) { return _template; }
62423                 if (source.id === 'custom') {
62424                     _template = val;
62425                 }
62426                 return source;
62427             };
62428
62429
62430             source.url = function(coord) {
62431                 var result = _template;
62432                 if (result === '') { return result; }   // source 'none'
62433
62434
62435                 // Guess a type based on the tokens present in the template
62436                 // (This is for 'custom' source, where we don't know)
62437                 if (!source.type) {
62438                     if (/\{(proj|wkid|bbox)\}/.test(_template)) {
62439                         source.type = 'wms';
62440                         source.projection = 'EPSG:3857';  // guess
62441                     } else if (/\{(x|y)\}/.test(_template)) {
62442                         source.type = 'tms';
62443                     } else if (/\{u\}/.test(_template)) {
62444                         source.type = 'bing';
62445                     }
62446                 }
62447
62448
62449                 if (source.type === 'wms') {
62450                     var tileToProjectedCoords = (function(x, y, z) {
62451                         //polyfill for IE11, PhantomJS
62452                         var sinh = Math.sinh || function(x) {
62453                             var y = Math.exp(x);
62454                             return (y - 1 / y) / 2;
62455                         };
62456
62457                         var zoomSize = Math.pow(2, z);
62458                         var lon = x / zoomSize * Math.PI * 2 - Math.PI;
62459                         var lat = Math.atan(sinh(Math.PI * (1 - 2 * y / zoomSize)));
62460
62461                         switch (source.projection) {
62462                             case 'EPSG:4326':
62463                                 return {
62464                                     x: lon * 180 / Math.PI,
62465                                     y: lat * 180 / Math.PI
62466                                 };
62467                             default: // EPSG:3857 and synonyms
62468                                 var mercCoords = mercatorRaw(lon, lat);
62469                                 return {
62470                                     x: 20037508.34 / Math.PI * mercCoords[0],
62471                                     y: 20037508.34 / Math.PI * mercCoords[1]
62472                                 };
62473                         }
62474                     });
62475
62476                     var tileSize = source.tileSize;
62477                     var projection = source.projection;
62478                     var minXmaxY = tileToProjectedCoords(coord[0], coord[1], coord[2]);
62479                     var maxXminY = tileToProjectedCoords(coord[0]+1, coord[1]+1, coord[2]);
62480
62481                     result = result.replace(/\{(\w+)\}/g, function (token, key) {
62482                       switch (key) {
62483                         case 'width':
62484                         case 'height':
62485                           return tileSize;
62486                         case 'proj':
62487                           return projection;
62488                         case 'wkid':
62489                           return projection.replace(/^EPSG:/, '');
62490                         case 'bbox':
62491                           return minXmaxY.x + ',' + maxXminY.y + ',' + maxXminY.x + ',' + minXmaxY.y;
62492                         case 'w':
62493                           return minXmaxY.x;
62494                         case 's':
62495                           return maxXminY.y;
62496                         case 'n':
62497                           return maxXminY.x;
62498                         case 'e':
62499                           return minXmaxY.y;
62500                         default:
62501                           return token;
62502                       }
62503                     });
62504
62505                 } else if (source.type === 'tms') {
62506                     result = result
62507                         .replace('{x}', coord[0])
62508                         .replace('{y}', coord[1])
62509                         // TMS-flipped y coordinate
62510                         .replace(/\{[t-]y\}/, Math.pow(2, coord[2]) - coord[1] - 1)
62511                         .replace(/\{z(oom)?\}/, coord[2])
62512                         // only fetch retina tiles for retina screens
62513                         .replace(/\{@2x\}|\{r\}/, isRetina ? '@2x' : '');
62514
62515                 } else if (source.type === 'bing') {
62516                     result = result
62517                         .replace('{u}', function() {
62518                             var u = '';
62519                             for (var zoom = coord[2]; zoom > 0; zoom--) {
62520                                 var b = 0;
62521                                 var mask = 1 << (zoom - 1);
62522                                 if ((coord[0] & mask) !== 0) { b++; }
62523                                 if ((coord[1] & mask) !== 0) { b += 2; }
62524                                 u += b.toString();
62525                             }
62526                             return u;
62527                         });
62528                 }
62529
62530                 // these apply to any type..
62531                 result = result.replace(/\{switch:([^}]+)\}/, function(s, r) {
62532                     var subdomains = r.split(',');
62533                     return subdomains[(coord[0] + coord[1]) % subdomains.length];
62534                 });
62535
62536
62537                 return result;
62538             };
62539
62540
62541             source.validZoom = function(z) {
62542                 return source.zoomExtent[0] <= z &&
62543                     (source.overzoom || source.zoomExtent[1] > z);
62544             };
62545
62546
62547             source.isLocatorOverlay = function() {
62548                 return source.id === 'mapbox_locator_overlay';
62549             };
62550
62551
62552             /* hides a source from the list, but leaves it available for use */
62553             source.isHidden = function() {
62554                 return source.id === 'DigitalGlobe-Premium-vintage' ||
62555                     source.id === 'DigitalGlobe-Standard-vintage';
62556             };
62557
62558
62559             source.copyrightNotices = function() {};
62560
62561
62562             source.getMetadata = function(center, tileCoord, callback) {
62563                 var vintage = {
62564                     start: localeDateString(source.startDate),
62565                     end: localeDateString(source.endDate)
62566                 };
62567                 vintage.range = vintageRange(vintage);
62568
62569                 var metadata = { vintage: vintage };
62570                 callback(null, metadata);
62571             };
62572
62573
62574             return source;
62575         }
62576
62577
62578         rendererBackgroundSource.Bing = function(data, dispatch) {
62579             // http://msdn.microsoft.com/en-us/library/ff701716.aspx
62580             // http://msdn.microsoft.com/en-us/library/ff701701.aspx
62581
62582             data.template = 'https://ecn.t{switch:0,1,2,3}.tiles.virtualearth.net/tiles/a{u}.jpeg?g=587&mkt=en-gb&n=z';
62583
62584             var bing = rendererBackgroundSource(data);
62585             // var key = 'Arzdiw4nlOJzRwOz__qailc8NiR31Tt51dN2D7cm57NrnceZnCpgOkmJhNpGoppU'; // P2, JOSM, etc
62586             var key = 'Ak5oTE46TUbjRp08OFVcGpkARErDobfpuyNKa-W2mQ8wbt1K1KL8p1bIRwWwcF-Q';    // iD
62587
62588
62589             var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial?include=ImageryProviders&key=' + key;
62590             var cache = {};
62591             var inflight = {};
62592             var providers = [];
62593
62594             d3_json(url)
62595                 .then(function(json) {
62596                     providers = json.resourceSets[0].resources[0].imageryProviders.map(function(provider) {
62597                         return {
62598                             attribution: provider.attribution,
62599                             areas: provider.coverageAreas.map(function(area) {
62600                                 return {
62601                                     zoom: [area.zoomMin, area.zoomMax],
62602                                     extent: geoExtent([area.bbox[1], area.bbox[0]], [area.bbox[3], area.bbox[2]])
62603                                 };
62604                             })
62605                         };
62606                     });
62607                     dispatch.call('change');
62608                 })
62609                 .catch(function() {
62610                     /* ignore */
62611                 });
62612
62613
62614             bing.copyrightNotices = function(zoom, extent) {
62615                 zoom = Math.min(zoom, 21);
62616                 return providers.filter(function(provider) {
62617                     return provider.areas.some(function(area) {
62618                         return extent.intersects(area.extent) &&
62619                             area.zoom[0] <= zoom &&
62620                             area.zoom[1] >= zoom;
62621                     });
62622                 }).map(function(provider) {
62623                     return provider.attribution;
62624                 }).join(', ');
62625             };
62626
62627
62628             bing.getMetadata = function(center, tileCoord, callback) {
62629                 var tileID = tileCoord.slice(0, 3).join('/');
62630                 var zoom = Math.min(tileCoord[2], 21);
62631                 var centerPoint = center[1] + ',' + center[0];  // lat,lng
62632                 var url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial/' + centerPoint +
62633                         '?zl=' + zoom + '&key=' + key;
62634
62635                 if (inflight[tileID]) { return; }
62636
62637                 if (!cache[tileID]) {
62638                     cache[tileID] = {};
62639                 }
62640                 if (cache[tileID] && cache[tileID].metadata) {
62641                     return callback(null, cache[tileID].metadata);
62642                 }
62643
62644                 inflight[tileID] = true;
62645                 d3_json(url)
62646                     .then(function(result) {
62647                         delete inflight[tileID];
62648                         if (!result) {
62649                             throw new Error('Unknown Error');
62650                         }
62651                         var vintage = {
62652                             start: localeDateString(result.resourceSets[0].resources[0].vintageStart),
62653                             end: localeDateString(result.resourceSets[0].resources[0].vintageEnd)
62654                         };
62655                         vintage.range = vintageRange(vintage);
62656
62657                         var metadata = { vintage: vintage };
62658                         cache[tileID].metadata = metadata;
62659                         if (callback) { callback(null, metadata); }
62660                     })
62661                     .catch(function(err) {
62662                         delete inflight[tileID];
62663                         if (callback) { callback(err.message); }
62664                     });
62665             };
62666
62667
62668             bing.terms_url = 'https://blog.openstreetmap.org/2010/11/30/microsoft-imagery-details';
62669
62670
62671             return bing;
62672         };
62673
62674
62675
62676         rendererBackgroundSource.Esri = function(data) {
62677             // in addition to using the tilemap at zoom level 20, overzoom real tiles - #4327 (deprecated technique, but it works)
62678             if (data.template.match(/blankTile/) === null) {
62679                 data.template = data.template + '?blankTile=false';
62680             }
62681
62682             var esri = rendererBackgroundSource(data);
62683             var cache = {};
62684             var inflight = {};
62685             var _prevCenter;
62686
62687             // use a tilemap service to set maximum zoom for esri tiles dynamically
62688             // https://developers.arcgis.com/documentation/tiled-elevation-service/
62689             esri.fetchTilemap = function(center) {
62690                 // skip if we have already fetched a tilemap within 5km
62691                 if (_prevCenter && geoSphericalDistance(center, _prevCenter) < 5000) { return; }
62692                 _prevCenter = center;
62693
62694                 // tiles are available globally to zoom level 19, afterward they may or may not be present
62695                 var z = 20;
62696
62697                 // first generate a random url using the template
62698                 var dummyUrl = esri.url([1,2,3]);
62699
62700                 // calculate url z/y/x from the lat/long of the center of the map
62701                 var x = (Math.floor((center[0] + 180) / 360 * Math.pow(2, z)));
62702                 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)));
62703
62704                 // fetch an 8x8 grid to leverage cache
62705                 var tilemapUrl = dummyUrl.replace(/tile\/[0-9]+\/[0-9]+\/[0-9]+\?blankTile=false/, 'tilemap') + '/' + z + '/' + y + '/' + x + '/8/8';
62706
62707                 // make the request and introspect the response from the tilemap server
62708                 d3_json(tilemapUrl)
62709                     .then(function(tilemap) {
62710                         if (!tilemap) {
62711                             throw new Error('Unknown Error');
62712                         }
62713                         var hasTiles = true;
62714                         for (var i = 0; i < tilemap.data.length; i++) {
62715                             // 0 means an individual tile in the grid doesn't exist
62716                             if (!tilemap.data[i]) {
62717                                 hasTiles = false;
62718                                 break;
62719                             }
62720                         }
62721
62722                         // if any tiles are missing at level 20 we restrict maxZoom to 19
62723                         esri.zoomExtent[1] = (hasTiles ? 22 : 19);
62724                     })
62725                     .catch(function() {
62726                         /* ignore */
62727                     });
62728             };
62729
62730
62731             esri.getMetadata = function(center, tileCoord, callback) {
62732                 var tileID = tileCoord.slice(0, 3).join('/');
62733                 var zoom = Math.min(tileCoord[2], esri.zoomExtent[1]);
62734                 var centerPoint = center[0] + ',' + center[1];  // long, lat (as it should be)
62735                 var unknown = _t('info_panels.background.unknown');
62736                 var metadataLayer;
62737                 var vintage = {};
62738                 var metadata = {};
62739
62740                 if (inflight[tileID]) { return; }
62741
62742                 switch (true) {
62743                     case (zoom >= 20 && esri.id === 'EsriWorldImageryClarity'):
62744                         metadataLayer = 4;
62745                         break;
62746                     case zoom >= 19:
62747                         metadataLayer = 3;
62748                         break;
62749                     case zoom >= 17:
62750                         metadataLayer = 2;
62751                         break;
62752                     case zoom >= 13:
62753                         metadataLayer = 0;
62754                         break;
62755                     default:
62756                         metadataLayer = 99;
62757                 }
62758
62759                 var url;
62760                 // build up query using the layer appropriate to the current zoom
62761                 if (esri.id === 'EsriWorldImagery') {
62762                     url = 'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/';
62763                 } else if (esri.id === 'EsriWorldImageryClarity') {
62764                     url = 'https://serviceslab.arcgisonline.com/arcgis/rest/services/Clarity_World_Imagery/MapServer/';
62765                 }
62766
62767                 url += metadataLayer + '/query?returnGeometry=false&geometry=' + centerPoint + '&inSR=4326&geometryType=esriGeometryPoint&outFields=*&f=json';
62768
62769                 if (!cache[tileID]) {
62770                     cache[tileID] = {};
62771                 }
62772                 if (cache[tileID] && cache[tileID].metadata) {
62773                     return callback(null, cache[tileID].metadata);
62774                 }
62775
62776                 // accurate metadata is only available >= 13
62777                 if (metadataLayer === 99) {
62778                     vintage = {
62779                         start: null,
62780                         end: null,
62781                         range: null
62782                     };
62783                     metadata = {
62784                         vintage: null,
62785                         source: unknown,
62786                         description: unknown,
62787                         resolution: unknown,
62788                         accuracy: unknown
62789                     };
62790
62791                     callback(null, metadata);
62792
62793                 } else {
62794                     inflight[tileID] = true;
62795                     d3_json(url)
62796                         .then(function(result) {
62797                             delete inflight[tileID];
62798                             if (!result) {
62799                                 throw new Error('Unknown Error');
62800                             } else if (result.features && result.features.length < 1) {
62801                                 throw new Error('No Results');
62802                             } else if (result.error && result.error.message) {
62803                                 throw new Error(result.error.message);
62804                             }
62805
62806                             // pass through the discrete capture date from metadata
62807                             var captureDate = localeDateString(result.features[0].attributes.SRC_DATE2);
62808                             vintage = {
62809                                 start: captureDate,
62810                                 end: captureDate,
62811                                 range: captureDate
62812                             };
62813                             metadata = {
62814                                 vintage: vintage,
62815                                 source: clean(result.features[0].attributes.NICE_NAME),
62816                                 description: clean(result.features[0].attributes.NICE_DESC),
62817                                 resolution: clean(+parseFloat(result.features[0].attributes.SRC_RES).toFixed(4)),
62818                                 accuracy: clean(+parseFloat(result.features[0].attributes.SRC_ACC).toFixed(4))
62819                             };
62820
62821                             // append units - meters
62822                             if (isFinite(metadata.resolution)) {
62823                                 metadata.resolution += ' m';
62824                             }
62825                             if (isFinite(metadata.accuracy)) {
62826                                 metadata.accuracy += ' m';
62827                             }
62828
62829                             cache[tileID].metadata = metadata;
62830                             if (callback) { callback(null, metadata); }
62831                         })
62832                         .catch(function(err) {
62833                             delete inflight[tileID];
62834                             if (callback) { callback(err.message); }
62835                         });
62836                 }
62837
62838
62839                 function clean(val) {
62840                     return String(val).trim() || unknown;
62841                 }
62842             };
62843
62844             return esri;
62845         };
62846
62847
62848         rendererBackgroundSource.None = function() {
62849             var source = rendererBackgroundSource({ id: 'none', template: '' });
62850
62851
62852             source.name = function() {
62853                 return _t('background.none');
62854             };
62855
62856
62857             source.imageryUsed = function() {
62858                 return null;
62859             };
62860
62861
62862             source.area = function() {
62863                 return -1;  // sources in background pane are sorted by area
62864             };
62865
62866
62867             return source;
62868         };
62869
62870
62871         rendererBackgroundSource.Custom = function(template) {
62872             var source = rendererBackgroundSource({ id: 'custom', template: template });
62873
62874
62875             source.name = function() {
62876                 return _t('background.custom');
62877             };
62878
62879
62880             source.imageryUsed = function() {
62881                 // sanitize personal connection tokens - #6801
62882                 var cleaned = source.template();
62883
62884                 // from query string parameters
62885                 if (cleaned.indexOf('?') !== -1) {
62886                     var parts = cleaned.split('?', 2);
62887                     var qs = utilStringQs(parts[1]);
62888
62889                     ['access_token', 'connectId', 'token'].forEach(function(param) {
62890                         if (qs[param]) {
62891                             qs[param] = '{apikey}';
62892                         }
62893                     });
62894                     cleaned = parts[0] + '?' + utilQsString(qs, true);  // true = soft encode
62895                 }
62896
62897                 // from wms/wmts api path parameters
62898                 cleaned = cleaned.replace(/token\/(\w+)/, 'token/{apikey}');
62899
62900                 return 'Custom (' + cleaned + ' )';
62901             };
62902
62903
62904             source.area = function() {
62905                 return -2;  // sources in background pane are sorted by area
62906             };
62907
62908
62909             return source;
62910         };
62911
62912         function rendererTileLayer(context) {
62913             var transformProp = utilPrefixCSSProperty('Transform');
62914             var tiler = utilTiler();
62915
62916             var _tileSize = 256;
62917             var _projection;
62918             var _cache = {};
62919             var _tileOrigin;
62920             var _zoom;
62921             var _source;
62922
62923
62924             function tileSizeAtZoom(d, z) {
62925                 var EPSILON = 0.002;    // close seams
62926                 return ((_tileSize * Math.pow(2, z - d[2])) / _tileSize) + EPSILON;
62927             }
62928
62929
62930             function atZoom(t, distance) {
62931                 var power = Math.pow(2, distance);
62932                 return [
62933                     Math.floor(t[0] * power),
62934                     Math.floor(t[1] * power),
62935                     t[2] + distance
62936                 ];
62937             }
62938
62939
62940             function lookUp(d) {
62941                 for (var up = -1; up > -d[2]; up--) {
62942                     var tile = atZoom(d, up);
62943                     if (_cache[_source.url(tile)] !== false) {
62944                         return tile;
62945                     }
62946                 }
62947             }
62948
62949
62950             function uniqueBy(a, n) {
62951                 var o = [];
62952                 var seen = {};
62953                 for (var i = 0; i < a.length; i++) {
62954                     if (seen[a[i][n]] === undefined) {
62955                         o.push(a[i]);
62956                         seen[a[i][n]] = true;
62957                     }
62958                 }
62959                 return o;
62960             }
62961
62962
62963             function addSource(d) {
62964                 d.push(_source.url(d));
62965                 return d;
62966             }
62967
62968
62969             // Update tiles based on current state of `projection`.
62970             function background(selection) {
62971                 _zoom = geoScaleToZoom(_projection.scale(), _tileSize);
62972
62973                 var pixelOffset;
62974                 if (_source) {
62975                     pixelOffset = [
62976                         _source.offset()[0] * Math.pow(2, _zoom),
62977                         _source.offset()[1] * Math.pow(2, _zoom)
62978                     ];
62979                 } else {
62980                     pixelOffset = [0, 0];
62981                 }
62982
62983                 var translate = [
62984                     _projection.translate()[0] + pixelOffset[0],
62985                     _projection.translate()[1] + pixelOffset[1]
62986                 ];
62987
62988                 tiler
62989                     .scale(_projection.scale() * 2 * Math.PI)
62990                     .translate(translate);
62991
62992                 _tileOrigin = [
62993                     _projection.scale() * Math.PI - translate[0],
62994                     _projection.scale() * Math.PI - translate[1]
62995                 ];
62996
62997                 render(selection);
62998             }
62999
63000
63001             // Derive the tiles onscreen, remove those offscreen and position them.
63002             // Important that this part not depend on `_projection` because it's
63003             // rentered when tiles load/error (see #644).
63004             function render(selection) {
63005                 if (!_source) { return; }
63006                 var requests = [];
63007                 var showDebug = context.getDebug('tile') && !_source.overlay;
63008
63009                 if (_source.validZoom(_zoom)) {
63010                     tiler.skipNullIsland(!!_source.overlay);
63011
63012                     tiler().forEach(function(d) {
63013                         addSource(d);
63014                         if (d[3] === '') { return; }
63015                         if (typeof d[3] !== 'string') { return; } // Workaround for #2295
63016                         requests.push(d);
63017                         if (_cache[d[3]] === false && lookUp(d)) {
63018                             requests.push(addSource(lookUp(d)));
63019                         }
63020                     });
63021
63022                     requests = uniqueBy(requests, 3).filter(function(r) {
63023                         // don't re-request tiles which have failed in the past
63024                         return _cache[r[3]] !== false;
63025                     });
63026                 }
63027
63028                 function load(d) {
63029                     _cache[d[3]] = true;
63030                     select(this)
63031                         .on('error', null)
63032                         .on('load', null)
63033                         .classed('tile-loaded', true);
63034                     render(selection);
63035                 }
63036
63037                 function error(d) {
63038                     _cache[d[3]] = false;
63039                     select(this)
63040                         .on('error', null)
63041                         .on('load', null)
63042                         .remove();
63043                     render(selection);
63044                 }
63045
63046                 function imageTransform(d) {
63047                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63048                     var scale = tileSizeAtZoom(d, _zoom);
63049                     return 'translate(' +
63050                         ((d[0] * ts) - _tileOrigin[0]) + 'px,' +
63051                         ((d[1] * ts) - _tileOrigin[1]) + 'px) ' +
63052                         'scale(' + scale + ',' + scale + ')';
63053                 }
63054
63055                 function tileCenter(d) {
63056                     var ts = _tileSize * Math.pow(2, _zoom - d[2]);
63057                     return [
63058                         ((d[0] * ts) - _tileOrigin[0] + (ts / 2)),
63059                         ((d[1] * ts) - _tileOrigin[1] + (ts / 2))
63060                     ];
63061                 }
63062
63063                 function debugTransform(d) {
63064                     var coord = tileCenter(d);
63065                     return 'translate(' + coord[0] + 'px,' + coord[1] + 'px)';
63066                 }
63067
63068
63069                 // Pick a representative tile near the center of the viewport
63070                 // (This is useful for sampling the imagery vintage)
63071                 var dims = tiler.size();
63072                 var mapCenter = [dims[0] / 2, dims[1] / 2];
63073                 var minDist = Math.max(dims[0], dims[1]);
63074                 var nearCenter;
63075
63076                 requests.forEach(function(d) {
63077                     var c = tileCenter(d);
63078                     var dist = geoVecLength(c, mapCenter);
63079                     if (dist < minDist) {
63080                         minDist = dist;
63081                         nearCenter = d;
63082                     }
63083                 });
63084
63085
63086                 var image = selection.selectAll('img')
63087                     .data(requests, function(d) { return d[3]; });
63088
63089                 image.exit()
63090                     .style(transformProp, imageTransform)
63091                     .classed('tile-removing', true)
63092                     .classed('tile-center', false)
63093                     .each(function() {
63094                         var tile = select(this);
63095                         window.setTimeout(function() {
63096                             if (tile.classed('tile-removing')) {
63097                                 tile.remove();
63098                             }
63099                         }, 300);
63100                     });
63101
63102                 image.enter()
63103                   .append('img')
63104                     .attr('class', 'tile')
63105                     .attr('draggable', 'false')
63106                     .style('width', _tileSize + 'px')
63107                     .style('height', _tileSize + 'px')
63108                     .attr('src', function(d) { return d[3]; })
63109                     .on('error', error)
63110                     .on('load', load)
63111                   .merge(image)
63112                     .style(transformProp, imageTransform)
63113                     .classed('tile-debug', showDebug)
63114                     .classed('tile-removing', false)
63115                     .classed('tile-center', function(d) { return d === nearCenter; });
63116
63117
63118
63119                 var debug = selection.selectAll('.tile-label-debug')
63120                     .data(showDebug ? requests : [], function(d) { return d[3]; });
63121
63122                 debug.exit()
63123                     .remove();
63124
63125                 if (showDebug) {
63126                     var debugEnter = debug.enter()
63127                         .append('div')
63128                         .attr('class', 'tile-label-debug');
63129
63130                     debugEnter
63131                         .append('div')
63132                         .attr('class', 'tile-label-debug-coord');
63133
63134                     debugEnter
63135                         .append('div')
63136                         .attr('class', 'tile-label-debug-vintage');
63137
63138                     debug = debug.merge(debugEnter);
63139
63140                     debug
63141                         .style(transformProp, debugTransform);
63142
63143                     debug
63144                         .selectAll('.tile-label-debug-coord')
63145                         .text(function(d) { return d[2] + ' / ' + d[0] + ' / ' + d[1]; });
63146
63147                     debug
63148                         .selectAll('.tile-label-debug-vintage')
63149                         .each(function(d) {
63150                             var span = select(this);
63151                             var center = context.projection.invert(tileCenter(d));
63152                             _source.getMetadata(center, d, function(err, result) {
63153                                 span.text((result && result.vintage && result.vintage.range) ||
63154                                     _t('info_panels.background.vintage') + ': ' + _t('info_panels.background.unknown')
63155                                 );
63156                             });
63157                         });
63158                 }
63159
63160             }
63161
63162
63163             background.projection = function(val) {
63164                 if (!arguments.length) { return _projection; }
63165                 _projection = val;
63166                 return background;
63167             };
63168
63169
63170             background.dimensions = function(val) {
63171                 if (!arguments.length) { return tiler.size(); }
63172                 tiler.size(val);
63173                 return background;
63174             };
63175
63176
63177             background.source = function(val) {
63178                 if (!arguments.length) { return _source; }
63179                 _source = val;
63180                 _tileSize = _source.tileSize;
63181                 _cache = {};
63182                 tiler.tileSize(_source.tileSize).zoomExtent(_source.zoomExtent);
63183                 return background;
63184             };
63185
63186
63187             return background;
63188         }
63189
63190         var _imageryIndex = null;
63191
63192         function rendererBackground(context) {
63193           var dispatch$1 = dispatch('change');
63194           var detected = utilDetect();
63195           var baseLayer = rendererTileLayer(context).projection(context.projection);
63196           var _isValid = true;
63197           var _overlayLayers = [];
63198           var _brightness = 1;
63199           var _contrast = 1;
63200           var _saturation = 1;
63201           var _sharpness = 1;
63202
63203
63204           function ensureImageryIndex() {
63205             return _mainFileFetcher.get('imagery')
63206               .then(function (sources) {
63207                 if (_imageryIndex) { return _imageryIndex; }
63208
63209                 _imageryIndex = {
63210                   imagery: sources,
63211                   features: {}
63212                 };
63213
63214                 // use which-polygon to support efficient index and querying for imagery
63215                 var features = sources.map(function (source) {
63216                   if (!source.polygon) { return null; }
63217                   // workaround for editor-layer-index weirdness..
63218                   // Add an extra array nest to each element in `source.polygon`
63219                   // so the rings are not treated as a bunch of holes:
63220                   // what we have: [ [[outer],[hole],[hole]] ]
63221                   // what we want: [ [[outer]],[[outer]],[[outer]] ]
63222                   var rings = source.polygon.map(function (ring) { return [ring]; });
63223
63224                   var feature = {
63225                     type: 'Feature',
63226                     properties: { id: source.id },
63227                     geometry: { type: 'MultiPolygon', coordinates: rings }
63228                   };
63229
63230                   _imageryIndex.features[source.id] = feature;
63231                   return feature;
63232
63233                 }).filter(Boolean);
63234
63235                 _imageryIndex.query = whichPolygon_1({ type: 'FeatureCollection', features: features });
63236
63237
63238                 // Instantiate `rendererBackgroundSource` objects for each source
63239                 _imageryIndex.backgrounds = sources.map(function (source) {
63240                   if (source.type === 'bing') {
63241                     return rendererBackgroundSource.Bing(source, dispatch$1);
63242                   } else if (/^EsriWorldImagery/.test(source.id)) {
63243                     return rendererBackgroundSource.Esri(source);
63244                   } else {
63245                     return rendererBackgroundSource(source);
63246                   }
63247                 });
63248
63249                 // Add 'None'
63250                 _imageryIndex.backgrounds.unshift(rendererBackgroundSource.None());
63251
63252                 // Add 'Custom'
63253                 var template = corePreferences('background-custom-template') || '';
63254                 var custom = rendererBackgroundSource.Custom(template);
63255                 _imageryIndex.backgrounds.unshift(custom);
63256
63257                 return _imageryIndex;
63258               });
63259           }
63260
63261
63262           function background(selection) {
63263             var currSource = baseLayer.source();
63264
63265             // If we are displaying an Esri basemap at high zoom,
63266             // check its tilemap to see how high the zoom can go
63267             if (context.map().zoom() > 18) {
63268               if (currSource && /^EsriWorldImagery/.test(currSource.id)) {
63269                 var center = context.map().center();
63270                 currSource.fetchTilemap(center);
63271               }
63272             }
63273
63274             // Is the imagery valid here? - #4827
63275             var sources = background.sources(context.map().extent());
63276             var wasValid = _isValid;
63277             _isValid = !!sources.filter(function (d) { return d === currSource; }).length;
63278
63279             if (wasValid !== _isValid) {      // change in valid status
63280               background.updateImagery();
63281             }
63282
63283
63284             var baseFilter = '';
63285             if (detected.cssfilters) {
63286               if (_brightness !== 1) {
63287                 baseFilter += " brightness(" + _brightness + ")";
63288               }
63289               if (_contrast !== 1) {
63290                 baseFilter += " contrast(" + _contrast + ")";
63291               }
63292               if (_saturation !== 1) {
63293                 baseFilter += " saturate(" + _saturation + ")";
63294               }
63295               if (_sharpness < 1) {  // gaussian blur
63296                 var blur = d3_interpolateNumber(0.5, 5)(1 - _sharpness);
63297                 baseFilter += " blur(" + blur + "px)";
63298               }
63299             }
63300
63301             var base = selection.selectAll('.layer-background')
63302               .data([0]);
63303
63304             base = base.enter()
63305               .insert('div', '.layer-data')
63306               .attr('class', 'layer layer-background')
63307               .merge(base);
63308
63309             if (detected.cssfilters) {
63310               base.style('filter', baseFilter || null);
63311             } else {
63312               base.style('opacity', _brightness);
63313             }
63314
63315
63316             var imagery = base.selectAll('.layer-imagery')
63317               .data([0]);
63318
63319             imagery.enter()
63320               .append('div')
63321               .attr('class', 'layer layer-imagery')
63322               .merge(imagery)
63323               .call(baseLayer);
63324
63325
63326             var maskFilter = '';
63327             var mixBlendMode = '';
63328             if (detected.cssfilters && _sharpness > 1) {  // apply unsharp mask
63329               mixBlendMode = 'overlay';
63330               maskFilter = 'saturate(0) blur(3px) invert(1)';
63331
63332               var contrast = _sharpness - 1;
63333               maskFilter += " contrast(" + contrast + ")";
63334
63335               var brightness = d3_interpolateNumber(1, 0.85)(_sharpness - 1);
63336               maskFilter += " brightness(" + brightness + ")";
63337             }
63338
63339             var mask = base.selectAll('.layer-unsharp-mask')
63340               .data(detected.cssfilters && _sharpness > 1 ? [0] : []);
63341
63342             mask.exit()
63343               .remove();
63344
63345             mask.enter()
63346               .append('div')
63347               .attr('class', 'layer layer-mask layer-unsharp-mask')
63348               .merge(mask)
63349               .call(baseLayer)
63350               .style('filter', maskFilter || null)
63351               .style('mix-blend-mode', mixBlendMode || null);
63352
63353
63354             var overlays = selection.selectAll('.layer-overlay')
63355               .data(_overlayLayers, function (d) { return d.source().name(); });
63356
63357             overlays.exit()
63358               .remove();
63359
63360             overlays.enter()
63361               .insert('div', '.layer-data')
63362               .attr('class', 'layer layer-overlay')
63363               .merge(overlays)
63364               .each(function (layer, i, nodes) { return select(nodes[i]).call(layer); });
63365           }
63366
63367
63368           background.updateImagery = function() {
63369             var currSource = baseLayer.source();
63370             if (context.inIntro() || !currSource) { return; }
63371
63372             var o = _overlayLayers
63373               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63374               .map(function (d) { return d.source().id; })
63375               .join(',');
63376
63377             var meters = geoOffsetToMeters(currSource.offset());
63378             var EPSILON = 0.01;
63379             var x = +meters[0].toFixed(2);
63380             var y = +meters[1].toFixed(2);
63381             var hash = utilStringQs(window.location.hash);
63382
63383             var id = currSource.id;
63384             if (id === 'custom') {
63385               id = "custom:" + (currSource.template());
63386             }
63387
63388             if (id) {
63389               hash.background = id;
63390             } else {
63391               delete hash.background;
63392             }
63393
63394             if (o) {
63395               hash.overlays = o;
63396             } else {
63397               delete hash.overlays;
63398             }
63399
63400             if (Math.abs(x) > EPSILON || Math.abs(y) > EPSILON) {
63401               hash.offset = x + "," + y;
63402             } else {
63403               delete hash.offset;
63404             }
63405
63406             if (!window.mocha) {
63407               window.location.replace('#' + utilQsString(hash, true));
63408             }
63409
63410             var imageryUsed = [];
63411             var photoOverlaysUsed = [];
63412
63413             var currUsed = currSource.imageryUsed();
63414             if (currUsed && _isValid) {
63415               imageryUsed.push(currUsed);
63416             }
63417
63418             _overlayLayers
63419               .filter(function (d) { return !d.source().isLocatorOverlay() && !d.source().isHidden(); })
63420               .forEach(function (d) { return imageryUsed.push(d.source().imageryUsed()); });
63421
63422             var dataLayer = context.layers().layer('data');
63423             if (dataLayer && dataLayer.enabled() && dataLayer.hasData()) {
63424               imageryUsed.push(dataLayer.getSrc());
63425             }
63426
63427             var photoOverlayLayers = {
63428               streetside: 'Bing Streetside',
63429               mapillary: 'Mapillary Images',
63430               'mapillary-map-features': 'Mapillary Map Features',
63431               'mapillary-signs': 'Mapillary Signs',
63432               openstreetcam: 'OpenStreetCam Images'
63433             };
63434
63435             for (var layerID in photoOverlayLayers) {
63436               var layer = context.layers().layer(layerID);
63437               if (layer && layer.enabled()) {
63438                 photoOverlaysUsed.push(layerID);
63439                 imageryUsed.push(photoOverlayLayers[layerID]);
63440               }
63441             }
63442
63443             context.history().imageryUsed(imageryUsed);
63444             context.history().photoOverlaysUsed(photoOverlaysUsed);
63445           };
63446
63447
63448           background.sources = function (extent, zoom, includeCurrent) {
63449             if (!_imageryIndex) { return []; }   // called before init()?
63450
63451             var visible = {};
63452             (_imageryIndex.query.bbox(extent.rectangle(), true) || [])
63453               .forEach(function (d) { return visible[d.id] = true; });
63454
63455             var currSource = baseLayer.source();
63456
63457             return _imageryIndex.backgrounds.filter(function (source) {
63458               if (!source.polygon) { return true; }                          // always include imagery with worldwide coverage
63459               if (includeCurrent && currSource === source) { return true; }  // optionally include the current imagery
63460               if (zoom && zoom < 6) { return false; }                        // optionally exclude local imagery at low zooms
63461               return visible[source.id];                                 // include imagery visible in given extent
63462             });
63463           };
63464
63465
63466           background.dimensions = function (val) {
63467             if (!val) { return; }
63468             baseLayer.dimensions(val);
63469             _overlayLayers.forEach(function (layer) { return layer.dimensions(val); });
63470           };
63471
63472
63473           background.baseLayerSource = function(d) {
63474             if (!arguments.length) { return baseLayer.source(); }
63475
63476             // test source against OSM imagery blacklists..
63477             var osm = context.connection();
63478             if (!osm) { return background; }
63479
63480             var blacklists = osm.imageryBlacklists();
63481             var template = d.template();
63482             var fail = false;
63483             var tested = 0;
63484             var regex;
63485
63486             for (var i = 0; i < blacklists.length; i++) {
63487               try {
63488                 regex = new RegExp(blacklists[i]);
63489                 fail = regex.test(template);
63490                 tested++;
63491                 if (fail) { break; }
63492               } catch (e) {
63493                 /* noop */
63494               }
63495             }
63496
63497             // ensure at least one test was run.
63498             if (!tested) {
63499               regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
63500               fail = regex.test(template);
63501             }
63502
63503             baseLayer.source(!fail ? d : background.findSource('none'));
63504             dispatch$1.call('change');
63505             background.updateImagery();
63506             return background;
63507           };
63508
63509
63510           background.findSource = function (id) {
63511             if (!id || !_imageryIndex) { return null; }   // called before init()?
63512             return _imageryIndex.backgrounds.find(function (d) { return d.id && d.id === id; });
63513           };
63514
63515
63516           background.bing = function () {
63517             background.baseLayerSource(background.findSource('Bing'));
63518           };
63519
63520
63521           background.showsLayer = function (d) {
63522             var currSource = baseLayer.source();
63523             if (!d || !currSource) { return false; }
63524             return d.id === currSource.id || _overlayLayers.some(function (layer) { return d.id === layer.source().id; });
63525           };
63526
63527
63528           background.overlayLayerSources = function () {
63529             return _overlayLayers.map(function (layer) { return layer.source(); });
63530           };
63531
63532
63533           background.toggleOverlayLayer = function (d) {
63534             var layer;
63535             for (var i = 0; i < _overlayLayers.length; i++) {
63536               layer = _overlayLayers[i];
63537               if (layer.source() === d) {
63538                 _overlayLayers.splice(i, 1);
63539                 dispatch$1.call('change');
63540                 background.updateImagery();
63541                 return;
63542               }
63543             }
63544
63545             layer = rendererTileLayer(context)
63546               .source(d)
63547               .projection(context.projection)
63548               .dimensions(baseLayer.dimensions()
63549             );
63550
63551             _overlayLayers.push(layer);
63552             dispatch$1.call('change');
63553             background.updateImagery();
63554           };
63555
63556
63557           background.nudge = function (d, zoom) {
63558             var currSource = baseLayer.source();
63559             if (currSource) {
63560               currSource.nudge(d, zoom);
63561               dispatch$1.call('change');
63562               background.updateImagery();
63563             }
63564             return background;
63565           };
63566
63567
63568           background.offset = function(d) {
63569             var currSource = baseLayer.source();
63570             if (!arguments.length) {
63571               return (currSource && currSource.offset()) || [0, 0];
63572             }
63573             if (currSource) {
63574               currSource.offset(d);
63575               dispatch$1.call('change');
63576               background.updateImagery();
63577             }
63578             return background;
63579           };
63580
63581
63582           background.brightness = function(d) {
63583             if (!arguments.length) { return _brightness; }
63584             _brightness = d;
63585             if (context.mode()) { dispatch$1.call('change'); }
63586             return background;
63587           };
63588
63589
63590           background.contrast = function(d) {
63591             if (!arguments.length) { return _contrast; }
63592             _contrast = d;
63593             if (context.mode()) { dispatch$1.call('change'); }
63594             return background;
63595           };
63596
63597
63598           background.saturation = function(d) {
63599             if (!arguments.length) { return _saturation; }
63600             _saturation = d;
63601             if (context.mode()) { dispatch$1.call('change'); }
63602             return background;
63603           };
63604
63605
63606           background.sharpness = function(d) {
63607             if (!arguments.length) { return _sharpness; }
63608             _sharpness = d;
63609             if (context.mode()) { dispatch$1.call('change'); }
63610             return background;
63611           };
63612
63613           var _loadPromise;
63614
63615           background.ensureLoaded = function () {
63616
63617             if (_loadPromise) { return _loadPromise; }
63618
63619             function parseMapParams(qmap) {
63620               if (!qmap) { return false; }
63621               var params = qmap.split('/').map(Number);
63622               if (params.length < 3 || params.some(isNaN)) { return false; }
63623               return geoExtent([params[2], params[1]]);  // lon,lat
63624             }
63625
63626             var hash = utilStringQs(window.location.hash);
63627             var requested = hash.background || hash.layer;
63628             var extent = parseMapParams(hash.map);
63629
63630             return _loadPromise = ensureImageryIndex()
63631               .then(function (imageryIndex) {
63632                 var first = imageryIndex.backgrounds.length && imageryIndex.backgrounds[0];
63633
63634                 var best;
63635                 if (!requested && extent) {
63636                   best = background.sources(extent).find(function (s) { return s.best(); });
63637                 }
63638
63639                 // Decide which background layer to display
63640                 if (requested && requested.indexOf('custom:') === 0) {
63641                   var template = requested.replace(/^custom:/, '');
63642                   var custom = background.findSource('custom');
63643                   background.baseLayerSource(custom.template(template));
63644                   corePreferences('background-custom-template', template);
63645                 } else {
63646                   background.baseLayerSource(
63647                     background.findSource(requested) ||
63648                     best ||
63649                     background.findSource(corePreferences('background-last-used')) ||
63650                     background.findSource('Bing') ||
63651                     first ||
63652                     background.findSource('none')
63653                   );
63654                 }
63655
63656                 var locator = imageryIndex.backgrounds.find(function (d) { return d.overlay && d.default; });
63657                 if (locator) {
63658                   background.toggleOverlayLayer(locator);
63659                 }
63660
63661                 var overlays = (hash.overlays || '').split(',');
63662                 overlays.forEach(function (overlay) {
63663                   overlay = background.findSource(overlay);
63664                   if (overlay) {
63665                     background.toggleOverlayLayer(overlay);
63666                   }
63667                 });
63668
63669                 if (hash.gpx) {
63670                   var gpx = context.layers().layer('data');
63671                   if (gpx) {
63672                     gpx.url(hash.gpx, '.gpx');
63673                   }
63674                 }
63675
63676                 if (hash.offset) {
63677                   var offset = hash.offset
63678                     .replace(/;/g, ',')
63679                     .split(',')
63680                     .map(function (n) { return !isNaN(n) && n; });
63681
63682                   if (offset.length === 2) {
63683                     background.offset(geoMetersToOffset(offset));
63684                   }
63685                 }
63686               })
63687               .catch(function () { /* ignore */ });
63688           };
63689
63690
63691           return utilRebind(background, dispatch$1, 'on');
63692         }
63693
63694         function rendererFeatures(context) {
63695             var dispatch$1 = dispatch('change', 'redraw');
63696             var features = utilRebind({}, dispatch$1, 'on');
63697             var _deferred = new Set();
63698
63699             var traffic_roads = {
63700                 'motorway': true,
63701                 'motorway_link': true,
63702                 'trunk': true,
63703                 'trunk_link': true,
63704                 'primary': true,
63705                 'primary_link': true,
63706                 'secondary': true,
63707                 'secondary_link': true,
63708                 'tertiary': true,
63709                 'tertiary_link': true,
63710                 'residential': true,
63711                 'unclassified': true,
63712                 'living_street': true
63713             };
63714
63715             var service_roads = {
63716                 'service': true,
63717                 'road': true,
63718                 'track': true
63719             };
63720
63721             var paths = {
63722                 'path': true,
63723                 'footway': true,
63724                 'cycleway': true,
63725                 'bridleway': true,
63726                 'steps': true,
63727                 'pedestrian': true
63728             };
63729
63730             var past_futures = {
63731                 'proposed': true,
63732                 'construction': true,
63733                 'abandoned': true,
63734                 'dismantled': true,
63735                 'disused': true,
63736                 'razed': true,
63737                 'demolished': true,
63738                 'obliterated': true
63739             };
63740
63741             var _cullFactor = 1;
63742             var _cache = {};
63743             var _rules = {};
63744             var _stats = {};
63745             var _keys = [];
63746             var _hidden = [];
63747             var _forceVisible = {};
63748
63749
63750             function update() {
63751                 if (!window.mocha) {
63752                     var hash = utilStringQs(window.location.hash);
63753                     var disabled = features.disabled();
63754                     if (disabled.length) {
63755                         hash.disable_features = disabled.join(',');
63756                     } else {
63757                         delete hash.disable_features;
63758                     }
63759                     window.location.replace('#' + utilQsString(hash, true));
63760                     corePreferences('disabled-features', disabled.join(','));
63761                 }
63762                 _hidden = features.hidden();
63763                 dispatch$1.call('change');
63764                 dispatch$1.call('redraw');
63765             }
63766
63767
63768             function defineRule(k, filter, max) {
63769                 var isEnabled = true;
63770
63771                 _keys.push(k);
63772                 _rules[k] = {
63773                     filter: filter,
63774                     enabled: isEnabled,   // whether the user wants it enabled..
63775                     count: 0,
63776                     currentMax: (max || Infinity),
63777                     defaultMax: (max || Infinity),
63778                     enable: function() { this.enabled = true; this.currentMax = this.defaultMax; },
63779                     disable: function() { this.enabled = false; this.currentMax = 0; },
63780                     hidden: function() {
63781                         return (this.count === 0 && !this.enabled) ||
63782                             this.count > this.currentMax * _cullFactor;
63783                     },
63784                     autoHidden: function() { return this.hidden() && this.currentMax > 0; }
63785                 };
63786             }
63787
63788
63789             defineRule('points', function isPoint(tags, geometry) {
63790                 return geometry === 'point';
63791             }, 200);
63792
63793             defineRule('traffic_roads', function isTrafficRoad(tags) {
63794                 return traffic_roads[tags.highway];
63795             });
63796
63797             defineRule('service_roads', function isServiceRoad(tags) {
63798                 return service_roads[tags.highway];
63799             });
63800
63801             defineRule('paths', function isPath(tags) {
63802                 return paths[tags.highway];
63803             });
63804
63805             defineRule('buildings', function isBuilding(tags) {
63806                 return (
63807                     (!!tags.building && tags.building !== 'no') ||
63808                     tags.parking === 'multi-storey' ||
63809                     tags.parking === 'sheds' ||
63810                     tags.parking === 'carports' ||
63811                     tags.parking === 'garage_boxes'
63812                 );
63813             }, 250);
63814
63815             defineRule('building_parts', function isBuildingPart(tags) {
63816                 return tags['building:part'];
63817             });
63818
63819             defineRule('indoor', function isIndoor(tags) {
63820                 return tags.indoor;
63821             });
63822
63823             defineRule('landuse', function isLanduse(tags, geometry) {
63824                 return geometry === 'area' &&
63825                     !_rules.buildings.filter(tags) &&
63826                     !_rules.building_parts.filter(tags) &&
63827                     !_rules.indoor.filter(tags) &&
63828                     !_rules.water.filter(tags) &&
63829                     !_rules.pistes.filter(tags);
63830             });
63831
63832             defineRule('boundaries', function isBoundary(tags) {
63833                 return (
63834                     !!tags.boundary
63835                 ) && !(
63836                     traffic_roads[tags.highway] ||
63837                     service_roads[tags.highway] ||
63838                     paths[tags.highway] ||
63839                     tags.waterway ||
63840                     tags.railway ||
63841                     tags.landuse ||
63842                     tags.natural ||
63843                     tags.building ||
63844                     tags.power
63845                 );
63846             });
63847
63848             defineRule('water', function isWater(tags) {
63849                 return (
63850                     !!tags.waterway ||
63851                     tags.natural === 'water' ||
63852                     tags.natural === 'coastline' ||
63853                     tags.natural === 'bay' ||
63854                     tags.landuse === 'pond' ||
63855                     tags.landuse === 'basin' ||
63856                     tags.landuse === 'reservoir' ||
63857                     tags.landuse === 'salt_pond'
63858                 );
63859             });
63860
63861             defineRule('rail', function isRail(tags) {
63862                 return (
63863                     !!tags.railway ||
63864                     tags.landuse === 'railway'
63865                 ) && !(
63866                     traffic_roads[tags.highway] ||
63867                     service_roads[tags.highway] ||
63868                     paths[tags.highway]
63869                 );
63870             });
63871
63872             defineRule('pistes', function isPiste(tags) {
63873                 return tags['piste:type'];
63874             });
63875
63876             defineRule('aerialways', function isPiste(tags) {
63877                 return tags.aerialway &&
63878                     tags.aerialway !== 'yes' &&
63879                     tags.aerialway !== 'station';
63880             });
63881
63882             defineRule('power', function isPower(tags) {
63883                 return !!tags.power;
63884             });
63885
63886             // contains a past/future tag, but not in active use as a road/path/cycleway/etc..
63887             defineRule('past_future', function isPastFuture(tags) {
63888                 if (
63889                     traffic_roads[tags.highway] ||
63890                     service_roads[tags.highway] ||
63891                     paths[tags.highway]
63892                 ) { return false; }
63893
63894                 var strings = Object.keys(tags);
63895
63896                 for (var i = 0; i < strings.length; i++) {
63897                     var s = strings[i];
63898                     if (past_futures[s] || past_futures[tags[s]]) { return true; }
63899                 }
63900                 return false;
63901             });
63902
63903             // Lines or areas that don't match another feature filter.
63904             // IMPORTANT: The 'others' feature must be the last one defined,
63905             //   so that code in getMatches can skip this test if `hasMatch = true`
63906             defineRule('others', function isOther(tags, geometry) {
63907                 return (geometry === 'line' || geometry === 'area');
63908             });
63909
63910
63911
63912             features.features = function() {
63913                 return _rules;
63914             };
63915
63916
63917             features.keys = function() {
63918                 return _keys;
63919             };
63920
63921
63922             features.enabled = function(k) {
63923                 if (!arguments.length) {
63924                     return _keys.filter(function(k) { return _rules[k].enabled; });
63925                 }
63926                 return _rules[k] && _rules[k].enabled;
63927             };
63928
63929
63930             features.disabled = function(k) {
63931                 if (!arguments.length) {
63932                     return _keys.filter(function(k) { return !_rules[k].enabled; });
63933                 }
63934                 return _rules[k] && !_rules[k].enabled;
63935             };
63936
63937
63938             features.hidden = function(k) {
63939                 if (!arguments.length) {
63940                     return _keys.filter(function(k) { return _rules[k].hidden(); });
63941                 }
63942                 return _rules[k] && _rules[k].hidden();
63943             };
63944
63945
63946             features.autoHidden = function(k) {
63947                 if (!arguments.length) {
63948                     return _keys.filter(function(k) { return _rules[k].autoHidden(); });
63949                 }
63950                 return _rules[k] && _rules[k].autoHidden();
63951             };
63952
63953
63954             features.enable = function(k) {
63955                 if (_rules[k] && !_rules[k].enabled) {
63956                     _rules[k].enable();
63957                     update();
63958                 }
63959             };
63960
63961             features.enableAll = function() {
63962                 var didEnable = false;
63963                 for (var k in _rules) {
63964                     if (!_rules[k].enabled) {
63965                         didEnable = true;
63966                         _rules[k].enable();
63967                     }
63968                 }
63969                 if (didEnable) { update(); }
63970             };
63971
63972
63973             features.disable = function(k) {
63974                 if (_rules[k] && _rules[k].enabled) {
63975                     _rules[k].disable();
63976                     update();
63977                 }
63978             };
63979
63980             features.disableAll = function() {
63981                 var didDisable = false;
63982                 for (var k in _rules) {
63983                     if (_rules[k].enabled) {
63984                         didDisable = true;
63985                         _rules[k].disable();
63986                     }
63987                 }
63988                 if (didDisable) { update(); }
63989             };
63990
63991
63992             features.toggle = function(k) {
63993                 if (_rules[k]) {
63994                     (function(f) { return f.enabled ? f.disable() : f.enable(); }(_rules[k]));
63995                     update();
63996                 }
63997             };
63998
63999
64000             features.resetStats = function() {
64001                 for (var i = 0; i < _keys.length; i++) {
64002                     _rules[_keys[i]].count = 0;
64003                 }
64004                 dispatch$1.call('change');
64005             };
64006
64007
64008             features.gatherStats = function(d, resolver, dimensions) {
64009                 var needsRedraw = false;
64010                 var types = utilArrayGroupBy(d, 'type');
64011                 var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64012                 var currHidden, geometry, matches, i, j;
64013
64014                 for (i = 0; i < _keys.length; i++) {
64015                     _rules[_keys[i]].count = 0;
64016                 }
64017
64018                 // adjust the threshold for point/building culling based on viewport size..
64019                 // a _cullFactor of 1 corresponds to a 1000x1000px viewport..
64020                 _cullFactor = dimensions[0] * dimensions[1] / 1000000;
64021
64022                 for (i = 0; i < entities.length; i++) {
64023                     geometry = entities[i].geometry(resolver);
64024                     matches = Object.keys(features.getMatches(entities[i], resolver, geometry));
64025                     for (j = 0; j < matches.length; j++) {
64026                         _rules[matches[j]].count++;
64027                     }
64028                 }
64029
64030                 currHidden = features.hidden();
64031                 if (currHidden !== _hidden) {
64032                     _hidden = currHidden;
64033                     needsRedraw = true;
64034                     dispatch$1.call('change');
64035                 }
64036
64037                 return needsRedraw;
64038             };
64039
64040
64041             features.stats = function() {
64042                 for (var i = 0; i < _keys.length; i++) {
64043                     _stats[_keys[i]] = _rules[_keys[i]].count;
64044                 }
64045
64046                 return _stats;
64047             };
64048
64049
64050             features.clear = function(d) {
64051                 for (var i = 0; i < d.length; i++) {
64052                     features.clearEntity(d[i]);
64053                 }
64054             };
64055
64056
64057             features.clearEntity = function(entity) {
64058                 delete _cache[osmEntity.key(entity)];
64059             };
64060
64061
64062             features.reset = function() {
64063                 Array.from(_deferred).forEach(function(handle) {
64064                     window.cancelIdleCallback(handle);
64065                     _deferred.delete(handle);
64066                 });
64067
64068                 _cache = {};
64069             };
64070
64071             // only certain relations are worth checking
64072             function relationShouldBeChecked(relation) {
64073                 // multipolygon features have `area` geometry and aren't checked here
64074                 return relation.tags.type === 'boundary';
64075             }
64076
64077             features.getMatches = function(entity, resolver, geometry) {
64078                 if (geometry === 'vertex' ||
64079                     (geometry === 'relation' && !relationShouldBeChecked(entity))) { return {}; }
64080
64081                 var ent = osmEntity.key(entity);
64082                 if (!_cache[ent]) {
64083                     _cache[ent] = {};
64084                 }
64085
64086                 if (!_cache[ent].matches) {
64087                     var matches = {};
64088                     var hasMatch = false;
64089
64090                     for (var i = 0; i < _keys.length; i++) {
64091                         if (_keys[i] === 'others') {
64092                             if (hasMatch) { continue; }
64093
64094                             // If an entity...
64095                             //   1. is a way that hasn't matched other 'interesting' feature rules,
64096                             if (entity.type === 'way') {
64097                                 var parents = features.getParents(entity, resolver, geometry);
64098
64099                                 //   2a. belongs only to a single multipolygon relation
64100                                 if ((parents.length === 1 && parents[0].isMultipolygon()) ||
64101                                     // 2b. or belongs only to boundary relations
64102                                     (parents.length > 0 && parents.every(function(parent) { return parent.tags.type === 'boundary'; }))) {
64103
64104                                     // ...then match whatever feature rules the parent relation has matched.
64105                                     // see #2548, #2887
64106                                     //
64107                                     // IMPORTANT:
64108                                     // For this to work, getMatches must be called on relations before ways.
64109                                     //
64110                                     var pkey = osmEntity.key(parents[0]);
64111                                     if (_cache[pkey] && _cache[pkey].matches) {
64112                                         matches = Object.assign({}, _cache[pkey].matches);  // shallow copy
64113                                         continue;
64114                                     }
64115                                 }
64116                             }
64117                         }
64118
64119                         if (_rules[_keys[i]].filter(entity.tags, geometry)) {
64120                             matches[_keys[i]] = hasMatch = true;
64121                         }
64122                     }
64123                     _cache[ent].matches = matches;
64124                 }
64125
64126                 return _cache[ent].matches;
64127             };
64128
64129
64130             features.getParents = function(entity, resolver, geometry) {
64131                 if (geometry === 'point') { return []; }
64132
64133                 var ent = osmEntity.key(entity);
64134                 if (!_cache[ent]) {
64135                     _cache[ent] = {};
64136                 }
64137
64138                 if (!_cache[ent].parents) {
64139                     var parents = [];
64140                     if (geometry === 'vertex') {
64141                         parents = resolver.parentWays(entity);
64142                     } else {   // 'line', 'area', 'relation'
64143                         parents = resolver.parentRelations(entity);
64144                     }
64145                     _cache[ent].parents = parents;
64146                 }
64147                 return _cache[ent].parents;
64148             };
64149
64150
64151             features.isHiddenPreset = function(preset, geometry) {
64152                 if (!_hidden.length) { return false; }
64153                 if (!preset.tags) { return false; }
64154
64155                 var test = preset.setTags({}, geometry);
64156                 for (var key in _rules) {
64157                     if (_rules[key].filter(test, geometry)) {
64158                         if (_hidden.indexOf(key) !== -1) {
64159                             return key;
64160                         }
64161                         return false;
64162                     }
64163                 }
64164                 return false;
64165             };
64166
64167
64168             features.isHiddenFeature = function(entity, resolver, geometry) {
64169                 if (!_hidden.length) { return false; }
64170                 if (!entity.version) { return false; }
64171                 if (_forceVisible[entity.id]) { return false; }
64172
64173                 var matches = Object.keys(features.getMatches(entity, resolver, geometry));
64174                 return matches.length && matches.every(function(k) { return features.hidden(k); });
64175             };
64176
64177
64178             features.isHiddenChild = function(entity, resolver, geometry) {
64179                 if (!_hidden.length) { return false; }
64180                 if (!entity.version || geometry === 'point') { return false; }
64181                 if (_forceVisible[entity.id]) { return false; }
64182
64183                 var parents = features.getParents(entity, resolver, geometry);
64184                 if (!parents.length) { return false; }
64185
64186                 for (var i = 0; i < parents.length; i++) {
64187                     if (!features.isHidden(parents[i], resolver, parents[i].geometry(resolver))) {
64188                         return false;
64189                     }
64190                 }
64191                 return true;
64192             };
64193
64194
64195             features.hasHiddenConnections = function(entity, resolver) {
64196                 if (!_hidden.length) { return false; }
64197
64198                 var childNodes, connections;
64199                 if (entity.type === 'midpoint') {
64200                     childNodes = [resolver.entity(entity.edge[0]), resolver.entity(entity.edge[1])];
64201                     connections = [];
64202                 } else {
64203                     childNodes = entity.nodes ? resolver.childNodes(entity) : [];
64204                     connections = features.getParents(entity, resolver, entity.geometry(resolver));
64205                 }
64206
64207                 // gather ways connected to child nodes..
64208                 connections = childNodes.reduce(function(result, e) {
64209                     return resolver.isShared(e) ? utilArrayUnion(result, resolver.parentWays(e)) : result;
64210                 }, connections);
64211
64212                 return connections.some(function(e) {
64213                     return features.isHidden(e, resolver, e.geometry(resolver));
64214                 });
64215             };
64216
64217
64218             features.isHidden = function(entity, resolver, geometry) {
64219                 if (!_hidden.length) { return false; }
64220                 if (!entity.version) { return false; }
64221
64222                 var fn = (geometry === 'vertex' ? features.isHiddenChild : features.isHiddenFeature);
64223                 return fn(entity, resolver, geometry);
64224             };
64225
64226
64227             features.filter = function(d, resolver) {
64228                 if (!_hidden.length) { return d; }
64229
64230                 var result = [];
64231                 for (var i = 0; i < d.length; i++) {
64232                     var entity = d[i];
64233                     if (!features.isHidden(entity, resolver, entity.geometry(resolver))) {
64234                         result.push(entity);
64235                     }
64236                 }
64237                 return result;
64238             };
64239
64240
64241             features.forceVisible = function(entityIDs) {
64242                 if (!arguments.length) { return Object.keys(_forceVisible); }
64243
64244                 _forceVisible = {};
64245                 for (var i = 0; i < entityIDs.length; i++) {
64246                     _forceVisible[entityIDs[i]] = true;
64247                     var entity = context.hasEntity(entityIDs[i]);
64248                     if (entity && entity.type === 'relation') {
64249                         // also show relation members (one level deep)
64250                         for (var j in entity.members) {
64251                             _forceVisible[entity.members[j].id] = true;
64252                         }
64253                     }
64254                 }
64255                 return features;
64256             };
64257
64258
64259             features.init = function() {
64260                 var storage = corePreferences('disabled-features');
64261                 if (storage) {
64262                     var storageDisabled = storage.replace(/;/g, ',').split(',');
64263                     storageDisabled.forEach(features.disable);
64264                 }
64265
64266                 var hash = utilStringQs(window.location.hash);
64267                 if (hash.disable_features) {
64268                     var hashDisabled = hash.disable_features.replace(/;/g, ',').split(',');
64269                     hashDisabled.forEach(features.disable);
64270                 }
64271             };
64272
64273
64274             // warm up the feature matching cache upon merging fetched data
64275             context.history().on('merge.features', function(newEntities) {
64276                 if (!newEntities) { return; }
64277                 var handle = window.requestIdleCallback(function() {
64278                     var graph = context.graph();
64279                     var types = utilArrayGroupBy(newEntities, 'type');
64280                     // ensure that getMatches is called on relations before ways
64281                     var entities = [].concat(types.relation || [], types.way || [], types.node || []);
64282                     for (var i = 0; i < entities.length; i++) {
64283                         var geometry = entities[i].geometry(graph);
64284                         features.getMatches(entities[i], graph, geometry);
64285                     }
64286                 });
64287                 _deferred.add(handle);
64288             });
64289
64290
64291             return features;
64292         }
64293
64294         // Touch targets control which other vertices we can drag a vertex onto.
64295         //
64296         // - the activeID - nope
64297         // - 1 away (adjacent) to the activeID - yes (vertices will be merged)
64298         // - 2 away from the activeID - nope (would create a self intersecting segment)
64299         // - all others on a linear way - yes
64300         // - all others on a closed way - nope (would create a self intersecting polygon)
64301         //
64302         // returns
64303         // 0 = active vertex - no touch/connect
64304         // 1 = passive vertex - yes touch/connect
64305         // 2 = adjacent vertex - yes but pay attention segmenting a line here
64306         //
64307         function svgPassiveVertex(node, graph, activeID) {
64308             if (!activeID) { return 1; }
64309             if (activeID === node.id) { return 0; }
64310
64311             var parents = graph.parentWays(node);
64312
64313             var i, j, nodes, isClosed, ix1, ix2, ix3, ix4, max;
64314
64315             for (i = 0; i < parents.length; i++) {
64316                 nodes = parents[i].nodes;
64317                 isClosed = parents[i].isClosed();
64318                 for (j = 0; j < nodes.length; j++) {   // find this vertex, look nearby
64319                     if (nodes[j] === node.id) {
64320                         ix1 = j - 2;
64321                         ix2 = j - 1;
64322                         ix3 = j + 1;
64323                         ix4 = j + 2;
64324
64325                         if (isClosed) {  // wraparound if needed
64326                             max = nodes.length - 1;
64327                             if (ix1 < 0)   { ix1 = max + ix1; }
64328                             if (ix2 < 0)   { ix2 = max + ix2; }
64329                             if (ix3 > max) { ix3 = ix3 - max; }
64330                             if (ix4 > max) { ix4 = ix4 - max; }
64331                         }
64332
64333                         if (nodes[ix1] === activeID) { return 0; }        // no - prevent self intersect
64334                         else if (nodes[ix2] === activeID) { return 2; }   // ok - adjacent
64335                         else if (nodes[ix3] === activeID) { return 2; }   // ok - adjacent
64336                         else if (nodes[ix4] === activeID) { return 0; }   // no - prevent self intersect
64337                         else if (isClosed && nodes.indexOf(activeID) !== -1) { return 0; }  // no - prevent self intersect
64338                     }
64339                 }
64340             }
64341
64342             return 1;   // ok
64343         }
64344
64345
64346         function svgMarkerSegments(projection, graph, dt,
64347                                           shouldReverse,
64348                                           bothDirections) {
64349             return function(entity) {
64350                 var i = 0;
64351                 var offset = dt;
64352                 var segments = [];
64353                 var clip = d3_geoIdentity().clipExtent(projection.clipExtent()).stream;
64354                 var coordinates = graph.childNodes(entity).map(function(n) { return n.loc; });
64355                 var a, b;
64356
64357                 if (shouldReverse(entity)) {
64358                     coordinates.reverse();
64359                 }
64360
64361                 d3_geoStream({
64362                     type: 'LineString',
64363                     coordinates: coordinates
64364                 }, projection.stream(clip({
64365                     lineStart: function() {},
64366                     lineEnd: function() { a = null; },
64367                     point: function(x, y) {
64368                         b = [x, y];
64369
64370                         if (a) {
64371                             var span = geoVecLength(a, b) - offset;
64372
64373                             if (span >= 0) {
64374                                 var heading = geoVecAngle(a, b);
64375                                 var dx = dt * Math.cos(heading);
64376                                 var dy = dt * Math.sin(heading);
64377                                 var p = [
64378                                     a[0] + offset * Math.cos(heading),
64379                                     a[1] + offset * Math.sin(heading)
64380                                 ];
64381
64382                                 // gather coordinates
64383                                 var coord = [a, p];
64384                                 for (span -= dt; span >= 0; span -= dt) {
64385                                     p = geoVecAdd(p, [dx, dy]);
64386                                     coord.push(p);
64387                                 }
64388                                 coord.push(b);
64389
64390                                 // generate svg paths
64391                                 var segment = '';
64392                                 var j;
64393
64394                                 for (j = 0; j < coord.length; j++) {
64395                                     segment += (j === 0 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64396                                 }
64397                                 segments.push({ id: entity.id, index: i++, d: segment });
64398
64399                                 if (bothDirections(entity)) {
64400                                     segment = '';
64401                                     for (j = coord.length - 1; j >= 0; j--) {
64402                                         segment += (j === coord.length - 1 ? 'M' : 'L') + coord[j][0] + ',' + coord[j][1];
64403                                     }
64404                                     segments.push({ id: entity.id, index: i++, d: segment });
64405                                 }
64406                             }
64407
64408                             offset = -span;
64409                         }
64410
64411                         a = b;
64412                     }
64413                 })));
64414
64415                 return segments;
64416             };
64417         }
64418
64419
64420         function svgPath(projection, graph, isArea) {
64421
64422             // Explanation of magic numbers:
64423             // "padding" here allows space for strokes to extend beyond the viewport,
64424             // so that the stroke isn't drawn along the edge of the viewport when
64425             // the shape is clipped.
64426             //
64427             // When drawing lines, pad viewport by 5px.
64428             // When drawing areas, pad viewport by 65px in each direction to allow
64429             // for 60px area fill stroke (see ".fill-partial path.fill" css rule)
64430
64431             var cache = {};
64432             var padding = isArea ? 65 : 5;
64433             var viewport = projection.clipExtent();
64434             var paddedExtent = [
64435                 [viewport[0][0] - padding, viewport[0][1] - padding],
64436                 [viewport[1][0] + padding, viewport[1][1] + padding]
64437             ];
64438             var clip = d3_geoIdentity().clipExtent(paddedExtent).stream;
64439             var project = projection.stream;
64440             var path = d3_geoPath()
64441                 .projection({stream: function(output) { return project(clip(output)); }});
64442
64443             var svgpath = function(entity) {
64444                 if (entity.id in cache) {
64445                     return cache[entity.id];
64446                 } else {
64447                     return cache[entity.id] = path(entity.asGeoJSON(graph));
64448                 }
64449             };
64450
64451             svgpath.geojson = function(d) {
64452                 if (d.__featurehash__ !== undefined) {
64453                     if (d.__featurehash__ in cache) {
64454                         return cache[d.__featurehash__];
64455                     } else {
64456                         return cache[d.__featurehash__] = path(d);
64457                     }
64458                 } else {
64459                     return path(d);
64460                 }
64461             };
64462
64463             return svgpath;
64464         }
64465
64466
64467         function svgPointTransform(projection) {
64468             var svgpoint = function(entity) {
64469                 // http://jsperf.com/short-array-join
64470                 var pt = projection(entity.loc);
64471                 return 'translate(' + pt[0] + ',' + pt[1] + ')';
64472             };
64473
64474             svgpoint.geojson = function(d) {
64475                 return svgpoint(d.properties.entity);
64476             };
64477
64478             return svgpoint;
64479         }
64480
64481
64482         function svgRelationMemberTags(graph) {
64483             return function(entity) {
64484                 var tags = entity.tags;
64485                 var shouldCopyMultipolygonTags = !entity.hasInterestingTags();
64486                 graph.parentRelations(entity).forEach(function(relation) {
64487                     var type = relation.tags.type;
64488                     if ((type === 'multipolygon' && shouldCopyMultipolygonTags) || type === 'boundary') {
64489                         tags = Object.assign({}, relation.tags, tags);
64490                     }
64491                 });
64492                 return tags;
64493             };
64494         }
64495
64496
64497         function svgSegmentWay(way, graph, activeID) {
64498             // When there is no activeID, we can memoize this expensive computation
64499             if (activeID === undefined) {
64500                 return graph.transient(way, 'waySegments', getWaySegments);
64501             } else {
64502                 return getWaySegments();
64503             }
64504
64505             function getWaySegments() {
64506                 var isActiveWay = (way.nodes.indexOf(activeID) !== -1);
64507                 var features = { passive: [], active: [] };
64508                 var start = {};
64509                 var end = {};
64510                 var node, type;
64511
64512                 for (var i = 0; i < way.nodes.length; i++) {
64513                     node = graph.entity(way.nodes[i]);
64514                     type = svgPassiveVertex(node, graph, activeID);
64515                     end = { node: node, type: type };
64516
64517                     if (start.type !== undefined) {
64518                         if (start.node.id === activeID || end.node.id === activeID) ; else if (isActiveWay && (start.type === 2 || end.type === 2)) {   // one adjacent vertex
64519                             pushActive(start, end, i);
64520                         } else if (start.type === 0 && end.type === 0) {   // both active vertices
64521                             pushActive(start, end, i);
64522                         } else {
64523                             pushPassive(start, end, i);
64524                         }
64525                     }
64526
64527                     start = end;
64528                 }
64529
64530                 return features;
64531
64532                 function pushActive(start, end, index) {
64533                     features.active.push({
64534                         type: 'Feature',
64535                         id: way.id + '-' + index + '-nope',
64536                         properties: {
64537                             nope: true,
64538                             target: true,
64539                             entity: way,
64540                             nodes: [start.node, end.node],
64541                             index: index
64542                         },
64543                         geometry: {
64544                             type: 'LineString',
64545                             coordinates: [start.node.loc, end.node.loc]
64546                         }
64547                     });
64548                 }
64549
64550                 function pushPassive(start, end, index) {
64551                     features.passive.push({
64552                         type: 'Feature',
64553                         id: way.id + '-' + index,
64554                         properties: {
64555                             target: true,
64556                             entity: way,
64557                             nodes: [start.node, end.node],
64558                             index: index
64559                         },
64560                         geometry: {
64561                             type: 'LineString',
64562                             coordinates: [start.node.loc, end.node.loc]
64563                         }
64564                     });
64565                 }
64566             }
64567         }
64568
64569         function svgTagClasses() {
64570             var primaries = [
64571                 'building', 'highway', 'railway', 'waterway', 'aeroway', 'aerialway',
64572                 'piste:type', 'boundary', 'power', 'amenity', 'natural', 'landuse',
64573                 'leisure', 'military', 'place', 'man_made', 'route', 'attraction',
64574                 'building:part', 'indoor'
64575             ];
64576             var statuses = [
64577                 // nonexistent, might be built
64578                 'proposed', 'planned',
64579                 // under maintentance or between groundbreaking and opening
64580                 'construction',
64581                 // existent but not functional
64582                 'disused',
64583                 // dilapidated to nonexistent
64584                 'abandoned',
64585                 // nonexistent, still may appear in imagery
64586                 'dismantled', 'razed', 'demolished', 'obliterated',
64587                 // existent occasionally, e.g. stormwater drainage basin
64588                 'intermittent'
64589             ];
64590             var secondaries = [
64591                 'oneway', 'bridge', 'tunnel', 'embankment', 'cutting', 'barrier',
64592                 'surface', 'tracktype', 'footway', 'crossing', 'service', 'sport',
64593                 'public_transport', 'location', 'parking', 'golf', 'type', 'leisure',
64594                 'man_made', 'indoor'
64595             ];
64596             var _tags = function(entity) { return entity.tags; };
64597
64598
64599             var tagClasses = function(selection) {
64600                 selection.each(function tagClassesEach(entity) {
64601                     var value = this.className;
64602
64603                     if (value.baseVal !== undefined) {
64604                         value = value.baseVal;
64605                     }
64606
64607                     var t = _tags(entity);
64608
64609                     var computed = tagClasses.getClassesString(t, value);
64610
64611                     if (computed !== value) {
64612                         select(this).attr('class', computed);
64613                     }
64614                 });
64615             };
64616
64617
64618             tagClasses.getClassesString = function(t, value) {
64619                 var primary, status;
64620                 var i, j, k, v;
64621
64622                 // in some situations we want to render perimeter strokes a certain way
64623                 var overrideGeometry;
64624                 if (/\bstroke\b/.test(value)) {
64625                     if (!!t.barrier && t.barrier !== 'no') {
64626                         overrideGeometry = 'line';
64627                     }
64628                 }
64629
64630                 // preserve base classes (nothing with `tag-`)
64631                 var classes = value.trim().split(/\s+/)
64632                     .filter(function(klass) {
64633                         return klass.length && !/^tag-/.test(klass);
64634                     })
64635                     .map(function(klass) {  // special overrides for some perimeter strokes
64636                         return (klass === 'line' || klass === 'area') ? (overrideGeometry || klass) : klass;
64637                     });
64638
64639                 // pick at most one primary classification tag..
64640                 for (i = 0; i < primaries.length; i++) {
64641                     k = primaries[i];
64642                     v = t[k];
64643                     if (!v || v === 'no') { continue; }
64644
64645                     if (k === 'piste:type') {  // avoid a ':' in the class name
64646                         k = 'piste';
64647                     } else if (k === 'building:part') {  // avoid a ':' in the class name
64648                         k = 'building_part';
64649                     }
64650
64651                     primary = k;
64652                     if (statuses.indexOf(v) !== -1) {   // e.g. `railway=abandoned`
64653                         status = v;
64654                         classes.push('tag-' + k);
64655                     } else {
64656                         classes.push('tag-' + k);
64657                         classes.push('tag-' + k + '-' + v);
64658                     }
64659
64660                     break;
64661                 }
64662
64663                 if (!primary) {
64664                     for (i = 0; i < statuses.length; i++) {
64665                         for (j = 0; j < primaries.length; j++) {
64666                             k = statuses[i] + ':' + primaries[j];  // e.g. `demolished:building=yes`
64667                             v = t[k];
64668                             if (!v || v === 'no') { continue; }
64669
64670                             status = statuses[i];
64671                             break;
64672                         }
64673                     }
64674                 }
64675
64676                 // add at most one status tag, only if relates to primary tag..
64677                 if (!status) {
64678                     for (i = 0; i < statuses.length; i++) {
64679                         k = statuses[i];
64680                         v = t[k];
64681                         if (!v || v === 'no') { continue; }
64682
64683                         if (v === 'yes') {   // e.g. `railway=rail + abandoned=yes`
64684                             status = k;
64685                         }
64686                         else if (primary && primary === v) {  // e.g. `railway=rail + abandoned=railway`
64687                             status = k;
64688                         } else if (!primary && primaries.indexOf(v) !== -1) {  // e.g. `abandoned=railway`
64689                             status = k;
64690                             primary = v;
64691                             classes.push('tag-' + v);
64692                         }  // else ignore e.g.  `highway=path + abandoned=railway`
64693
64694                         if (status) { break; }
64695                     }
64696                 }
64697
64698                 if (status) {
64699                     classes.push('tag-status');
64700                     classes.push('tag-status-' + status);
64701                 }
64702
64703                 // add any secondary tags
64704                 for (i = 0; i < secondaries.length; i++) {
64705                     k = secondaries[i];
64706                     v = t[k];
64707                     if (!v || v === 'no' || k === primary) { continue; }
64708                     classes.push('tag-' + k);
64709                     classes.push('tag-' + k + '-' + v);
64710                 }
64711
64712                 // For highways, look for surface tagging..
64713                 if ((primary === 'highway' && !osmPathHighwayTagValues[t.highway]) || primary === 'aeroway') {
64714                     var surface = t.highway === 'track' ? 'unpaved' : 'paved';
64715                     for (k in t) {
64716                         v = t[k];
64717                         if (k in osmPavedTags) {
64718                             surface = osmPavedTags[k][v] ? 'paved' : 'unpaved';
64719                         }
64720                         if (k in osmSemipavedTags && !!osmSemipavedTags[k][v]) {
64721                             surface = 'semipaved';
64722                         }
64723                     }
64724                     classes.push('tag-' + surface);
64725                 }
64726
64727                 // If this is a wikidata-tagged item, add a class for that..
64728                 if (t.wikidata || t['brand:wikidata']) {
64729                     classes.push('tag-wikidata');
64730                 }
64731
64732                 return classes.join(' ').trim();
64733             };
64734
64735
64736             tagClasses.tags = function(val) {
64737                 if (!arguments.length) { return _tags; }
64738                 _tags = val;
64739                 return tagClasses;
64740             };
64741
64742             return tagClasses;
64743         }
64744
64745         // Patterns only work in Firefox when set directly on element.
64746         // (This is not a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=750632)
64747         var patterns = {
64748             // tag - pattern name
64749             // -or-
64750             // tag - value - pattern name
64751             // -or-
64752             // tag - value - rules (optional tag-values, pattern name)
64753             // (matches earlier rules first, so fallback should be last entry)
64754             amenity: {
64755                 grave_yard: 'cemetery',
64756                 fountain: 'water_standing'
64757             },
64758             landuse: {
64759                 cemetery: [
64760                     { religion: 'christian', pattern: 'cemetery_christian' },
64761                     { religion: 'buddhist', pattern: 'cemetery_buddhist' },
64762                     { religion: 'muslim', pattern: 'cemetery_muslim' },
64763                     { religion: 'jewish', pattern: 'cemetery_jewish' },
64764                     { pattern: 'cemetery' }
64765                 ],
64766                 construction: 'construction',
64767                 farmland: 'farmland',
64768                 farmyard: 'farmyard',
64769                 forest: [
64770                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64771                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64772                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64773                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64774                 ],
64775                 grave_yard: 'cemetery',
64776                 grass: [
64777                     { golf: 'green', pattern: 'golf_green' },
64778                     { pattern: 'grass' } ],
64779                 landfill: 'landfill',
64780                 meadow: 'meadow',
64781                 military: 'construction',
64782                 orchard: 'orchard',
64783                 quarry: 'quarry',
64784                 vineyard: 'vineyard'
64785             },
64786             natural: {
64787                 beach: 'beach',
64788                 grassland: 'grass',
64789                 sand: 'beach',
64790                 scrub: 'scrub',
64791                 water: [
64792                     { water: 'pond', pattern: 'pond' },
64793                     { water: 'reservoir', pattern: 'water_standing' },
64794                     { pattern: 'waves' }
64795                 ],
64796                 wetland: [
64797                     { wetland: 'marsh', pattern: 'wetland_marsh' },
64798                     { wetland: 'swamp', pattern: 'wetland_swamp' },
64799                     { wetland: 'bog', pattern: 'wetland_bog' },
64800                     { wetland: 'reedbed', pattern: 'wetland_reedbed' },
64801                     { pattern: 'wetland' }
64802                 ],
64803                 wood: [
64804                     { leaf_type: 'broadleaved', pattern: 'forest_broadleaved' },
64805                     { leaf_type: 'needleleaved', pattern: 'forest_needleleaved' },
64806                     { leaf_type: 'leafless', pattern: 'forest_leafless' },
64807                     { pattern: 'forest' } // same as 'leaf_type:mixed'
64808                 ]
64809             },
64810             traffic_calming: {
64811                 island: [
64812                     { surface: 'grass', pattern: 'grass' } ],
64813                 chicane: [
64814                     { surface: 'grass', pattern: 'grass' } ],
64815                 choker: [
64816                     { surface: 'grass', pattern: 'grass' } ]
64817             }
64818         };
64819
64820         function svgTagPattern(tags) {
64821             // Skip pattern filling if this is a building (buildings don't get patterns applied)
64822             if (tags.building && tags.building !== 'no') {
64823                 return null;
64824             }
64825
64826             for (var tag in patterns) {
64827                 var entityValue = tags[tag];
64828                 if (!entityValue) { continue; }
64829
64830                 if (typeof patterns[tag] === 'string') { // extra short syntax (just tag) - pattern name
64831                     return 'pattern-' + patterns[tag];
64832                 } else {
64833                     var values = patterns[tag];
64834                     for (var value in values) {
64835                         if (entityValue !== value) { continue; }
64836
64837                         var rules = values[value];
64838                         if (typeof rules === 'string') { // short syntax - pattern name
64839                             return 'pattern-' + rules;
64840                         }
64841
64842                         // long syntax - rule array
64843                         for (var ruleKey in rules) {
64844                             var rule = rules[ruleKey];
64845
64846                             var pass = true;
64847                             for (var criterion in rule) {
64848                                 if (criterion !== 'pattern') { // reserved for pattern name
64849                                     // The only rule is a required tag-value pair
64850                                     var v = tags[criterion];
64851                                     if (!v || v !== rule[criterion]) {
64852                                         pass = false;
64853                                         break;
64854                                     }
64855                                 }
64856                             }
64857
64858                             if (pass) {
64859                                 return 'pattern-' + rule.pattern;
64860                             }
64861                         }
64862                     }
64863                 }
64864             }
64865
64866             return null;
64867         }
64868
64869         function svgAreas(projection, context) {
64870
64871
64872             function getPatternStyle(tags) {
64873                 var imageID = svgTagPattern(tags);
64874                 if (imageID) {
64875                     return 'url("#ideditor-' + imageID + '")';
64876                 }
64877                 return '';
64878             }
64879
64880
64881             function drawTargets(selection, graph, entities, filter) {
64882                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
64883                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
64884                 var getPath = svgPath(projection).geojson;
64885                 var activeID = context.activeID();
64886                 var base = context.history().base();
64887
64888                 // The targets and nopes will be MultiLineString sub-segments of the ways
64889                 var data = { targets: [], nopes: [] };
64890
64891                 entities.forEach(function(way) {
64892                     var features = svgSegmentWay(way, graph, activeID);
64893                     data.targets.push.apply(data.targets, features.passive);
64894                     data.nopes.push.apply(data.nopes, features.active);
64895                 });
64896
64897
64898                 // Targets allow hover and vertex snapping
64899                 var targetData = data.targets.filter(getPath);
64900                 var targets = selection.selectAll('.area.target-allowed')
64901                     .filter(function(d) { return filter(d.properties.entity); })
64902                     .data(targetData, function key(d) { return d.id; });
64903
64904                 // exit
64905                 targets.exit()
64906                     .remove();
64907
64908                 var segmentWasEdited = function(d) {
64909                     var wayID = d.properties.entity.id;
64910                     // if the whole line was edited, don't draw segment changes
64911                     if (!base.entities[wayID] ||
64912                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
64913                         return false;
64914                     }
64915                     return d.properties.nodes.some(function(n) {
64916                         return !base.entities[n.id] ||
64917                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
64918                     });
64919                 };
64920
64921                 // enter/update
64922                 targets.enter()
64923                     .append('path')
64924                     .merge(targets)
64925                     .attr('d', getPath)
64926                     .attr('class', function(d) { return 'way area target target-allowed ' + targetClass + d.id; })
64927                     .classed('segment-edited', segmentWasEdited);
64928
64929
64930                 // NOPE
64931                 var nopeData = data.nopes.filter(getPath);
64932                 var nopes = selection.selectAll('.area.target-nope')
64933                     .filter(function(d) { return filter(d.properties.entity); })
64934                     .data(nopeData, function key(d) { return d.id; });
64935
64936                 // exit
64937                 nopes.exit()
64938                     .remove();
64939
64940                 // enter/update
64941                 nopes.enter()
64942                     .append('path')
64943                     .merge(nopes)
64944                     .attr('d', getPath)
64945                     .attr('class', function(d) { return 'way area target target-nope ' + nopeClass + d.id; })
64946                     .classed('segment-edited', segmentWasEdited);
64947             }
64948
64949
64950             function drawAreas(selection, graph, entities, filter) {
64951                 var path = svgPath(projection, graph, true);
64952                 var areas = {};
64953                 var multipolygon;
64954                 var base = context.history().base();
64955
64956                 for (var i = 0; i < entities.length; i++) {
64957                     var entity = entities[i];
64958                     if (entity.geometry(graph) !== 'area') { continue; }
64959
64960                     multipolygon = osmIsOldMultipolygonOuterMember(entity, graph);
64961                     if (multipolygon) {
64962                         areas[multipolygon.id] = {
64963                             entity: multipolygon.mergeTags(entity.tags),
64964                             area: Math.abs(entity.area(graph))
64965                         };
64966                     } else if (!areas[entity.id]) {
64967                         areas[entity.id] = {
64968                             entity: entity,
64969                             area: Math.abs(entity.area(graph))
64970                         };
64971                     }
64972                 }
64973
64974                 var fills = Object.values(areas).filter(function hasPath(a) { return path(a.entity); });
64975                 fills.sort(function areaSort(a, b) { return b.area - a.area; });
64976                 fills = fills.map(function(a) { return a.entity; });
64977
64978                 var strokes = fills.filter(function(area) { return area.type === 'way'; });
64979
64980                 var data = {
64981                     clip: fills,
64982                     shadow: strokes,
64983                     stroke: strokes,
64984                     fill: fills
64985                 };
64986
64987                 var clipPaths = context.surface().selectAll('defs').selectAll('.clipPath-osm')
64988                    .filter(filter)
64989                    .data(data.clip, osmEntity.key);
64990
64991                 clipPaths.exit()
64992                    .remove();
64993
64994                 var clipPathsEnter = clipPaths.enter()
64995                    .append('clipPath')
64996                    .attr('class', 'clipPath-osm')
64997                    .attr('id', function(entity) { return 'ideditor-' + entity.id + '-clippath'; });
64998
64999                 clipPathsEnter
65000                    .append('path');
65001
65002                 clipPaths.merge(clipPathsEnter)
65003                    .selectAll('path')
65004                    .attr('d', path);
65005
65006
65007                 var drawLayer = selection.selectAll('.layer-osm.areas');
65008                 var touchLayer = selection.selectAll('.layer-touch.areas');
65009
65010                 // Draw areas..
65011                 var areagroup = drawLayer
65012                     .selectAll('g.areagroup')
65013                     .data(['fill', 'shadow', 'stroke']);
65014
65015                 areagroup = areagroup.enter()
65016                     .append('g')
65017                     .attr('class', function(d) { return 'areagroup area-' + d; })
65018                     .merge(areagroup);
65019
65020                 var paths = areagroup
65021                     .selectAll('path')
65022                     .filter(filter)
65023                     .data(function(layer) { return data[layer]; }, osmEntity.key);
65024
65025                 paths.exit()
65026                     .remove();
65027
65028
65029                 var fillpaths = selection.selectAll('.area-fill path.area').nodes();
65030                 var bisect = d3_bisector(function(node) { return -node.__data__.area(graph); }).left;
65031
65032                 function sortedByArea(entity) {
65033                     if (this._parent.__data__ === 'fill') {
65034                         return fillpaths[bisect(fillpaths, -entity.area(graph))];
65035                     }
65036                 }
65037
65038                 paths = paths.enter()
65039                     .insert('path', sortedByArea)
65040                     .merge(paths)
65041                     .each(function(entity) {
65042                         var layer = this.parentNode.__data__;
65043                         this.setAttribute('class', entity.type + ' area ' + layer + ' ' + entity.id);
65044
65045                         if (layer === 'fill') {
65046                             this.setAttribute('clip-path', 'url(#ideditor-' + entity.id + '-clippath)');
65047                             this.style.fill = this.style.stroke = getPatternStyle(entity.tags);
65048                         }
65049                     })
65050                     .classed('added', function(d) {
65051                         return !base.entities[d.id];
65052                     })
65053                     .classed('geometry-edited', function(d) {
65054                         return graph.entities[d.id] &&
65055                             base.entities[d.id] &&
65056                             !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
65057                     })
65058                     .classed('retagged', function(d) {
65059                         return graph.entities[d.id] &&
65060                             base.entities[d.id] &&
65061                             !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
65062                     })
65063                     .call(svgTagClasses())
65064                     .attr('d', path);
65065
65066
65067                 // Draw touch targets..
65068                 touchLayer
65069                     .call(drawTargets, graph, data.stroke, filter);
65070             }
65071
65072             return drawAreas;
65073         }
65074
65075         //[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]
65076         //[4a]          NameChar           ::=          NameStartChar | "-" | "." | [0-9] | #xB7 | [#x0300-#x036F] | [#x203F-#x2040]
65077         //[5]           Name       ::=          NameStartChar (NameChar)*
65078         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
65079         var nameChar = new RegExp("[\\-\\.0-9"+nameStartChar.source.slice(1,-1)+"\\u00B7\\u0300-\\u036F\\u203F-\\u2040]");
65080         var tagNamePattern = new RegExp('^'+nameStartChar.source+nameChar.source+'*(?:\:'+nameStartChar.source+nameChar.source+'*)?$');
65081         //var tagNamePattern = /^[a-zA-Z_][\w\-\.]*(?:\:[a-zA-Z_][\w\-\.]*)?$/
65082         //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(',')
65083
65084         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65085         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65086         var S_TAG = 0;//tag name offerring
65087         var S_ATTR = 1;//attr name offerring 
65088         var S_ATTR_SPACE=2;//attr name end and space offer
65089         var S_EQ = 3;//=space?
65090         var S_ATTR_NOQUOT_VALUE = 4;//attr value(no quot value only)
65091         var S_ATTR_END = 5;//attr value end and no space(quot end)
65092         var S_TAG_SPACE = 6;//(attr value end || tag end ) && (space offer)
65093         var S_TAG_CLOSE = 7;//closed el<el />
65094
65095         function XMLReader(){
65096                 
65097         }
65098
65099         XMLReader.prototype = {
65100                 parse:function(source,defaultNSMap,entityMap){
65101                         var domBuilder = this.domBuilder;
65102                         domBuilder.startDocument();
65103                         _copy(defaultNSMap ,defaultNSMap = {});
65104                         parse(source,defaultNSMap,entityMap,
65105                                         domBuilder,this.errorHandler);
65106                         domBuilder.endDocument();
65107                 }
65108         };
65109         function parse(source,defaultNSMapCopy,entityMap,domBuilder,errorHandler){
65110                 function fixedFromCharCode(code) {
65111                         // String.prototype.fromCharCode does not supports
65112                         // > 2 bytes unicode chars directly
65113                         if (code > 0xffff) {
65114                                 code -= 0x10000;
65115                                 var surrogate1 = 0xd800 + (code >> 10)
65116                                         , surrogate2 = 0xdc00 + (code & 0x3ff);
65117
65118                                 return String.fromCharCode(surrogate1, surrogate2);
65119                         } else {
65120                                 return String.fromCharCode(code);
65121                         }
65122                 }
65123                 function entityReplacer(a){
65124                         var k = a.slice(1,-1);
65125                         if(k in entityMap){
65126                                 return entityMap[k]; 
65127                         }else if(k.charAt(0) === '#'){
65128                                 return fixedFromCharCode(parseInt(k.substr(1).replace('x','0x')))
65129                         }else {
65130                                 errorHandler.error('entity not found:'+a);
65131                                 return a;
65132                         }
65133                 }
65134                 function appendText(end){//has some bugs
65135                         if(end>start){
65136                                 var xt = source.substring(start,end).replace(/&#?\w+;/g,entityReplacer);
65137                                 locator&&position(start);
65138                                 domBuilder.characters(xt,0,end-start);
65139                                 start = end;
65140                         }
65141                 }
65142                 function position(p,m){
65143                         while(p>=lineEnd && (m = linePattern.exec(source))){
65144                                 lineStart = m.index;
65145                                 lineEnd = lineStart + m[0].length;
65146                                 locator.lineNumber++;
65147                                 //console.log('line++:',locator,startPos,endPos)
65148                         }
65149                         locator.columnNumber = p-lineStart+1;
65150                 }
65151                 var lineStart = 0;
65152                 var lineEnd = 0;
65153                 var linePattern = /.*(?:\r\n?|\n)|.*$/g;
65154                 var locator = domBuilder.locator;
65155                 
65156                 var parseStack = [{currentNSMap:defaultNSMapCopy}];
65157                 var closeMap = {};
65158                 var start = 0;
65159                 while(true){
65160                         try{
65161                                 var tagStart = source.indexOf('<',start);
65162                                 if(tagStart<0){
65163                                         if(!source.substr(start).match(/^\s*$/)){
65164                                                 var doc = domBuilder.doc;
65165                                         var text = doc.createTextNode(source.substr(start));
65166                                         doc.appendChild(text);
65167                                         domBuilder.currentElement = text;
65168                                         }
65169                                         return;
65170                                 }
65171                                 if(tagStart>start){
65172                                         appendText(tagStart);
65173                                 }
65174                                 switch(source.charAt(tagStart+1)){
65175                                 case '/':
65176                                         var end = source.indexOf('>',tagStart+3);
65177                                         var tagName = source.substring(tagStart+2,end);
65178                                         var config = parseStack.pop();
65179                                         if(end<0){
65180                                                 
65181                                         tagName = source.substring(tagStart+2).replace(/[\s<].*/,'');
65182                                         //console.error('#@@@@@@'+tagName)
65183                                         errorHandler.error("end tag name: "+tagName+' is not complete:'+config.tagName);
65184                                         end = tagStart+1+tagName.length;
65185                                 }else if(tagName.match(/\s</)){
65186                                         tagName = tagName.replace(/[\s<].*/,'');
65187                                         errorHandler.error("end tag name: "+tagName+' maybe not complete');
65188                                         end = tagStart+1+tagName.length;
65189                                         }
65190                                         //console.error(parseStack.length,parseStack)
65191                                         //console.error(config);
65192                                         var localNSMap = config.localNSMap;
65193                                         var endMatch = config.tagName == tagName;
65194                                         var endIgnoreCaseMach = endMatch || config.tagName&&config.tagName.toLowerCase() == tagName.toLowerCase();
65195                                 if(endIgnoreCaseMach){
65196                                         domBuilder.endElement(config.uri,config.localName,tagName);
65197                                                 if(localNSMap){
65198                                                         for(var prefix in localNSMap){
65199                                                                 domBuilder.endPrefixMapping(prefix) ;
65200                                                         }
65201                                                 }
65202                                                 if(!endMatch){
65203                                         errorHandler.fatalError("end tag name: "+tagName+' is not match the current start tagName:'+config.tagName );
65204                                                 }
65205                                 }else {
65206                                         parseStack.push(config);
65207                                 }
65208                                         
65209                                         end++;
65210                                         break;
65211                                         // end elment
65212                                 case '?':// <?...?>
65213                                         locator&&position(tagStart);
65214                                         end = parseInstruction(source,tagStart,domBuilder);
65215                                         break;
65216                                 case '!':// <!doctype,<![CDATA,<!--
65217                                         locator&&position(tagStart);
65218                                         end = parseDCC(source,tagStart,domBuilder,errorHandler);
65219                                         break;
65220                                 default:
65221                                         locator&&position(tagStart);
65222                                         var el = new ElementAttributes();
65223                                         var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65224                                         //elStartEnd
65225                                         var end = parseElementStartPart(source,tagStart,el,currentNSMap,entityReplacer,errorHandler);
65226                                         var len = el.length;
65227                                         
65228                                         
65229                                         if(!el.closed && fixSelfClosed(source,end,el.tagName,closeMap)){
65230                                                 el.closed = true;
65231                                                 if(!entityMap.nbsp){
65232                                                         errorHandler.warning('unclosed xml attribute');
65233                                                 }
65234                                         }
65235                                         if(locator && len){
65236                                                 var locator2 = copyLocator(locator,{});
65237                                                 //try{//attribute position fixed
65238                                                 for(var i = 0;i<len;i++){
65239                                                         var a = el[i];
65240                                                         position(a.offset);
65241                                                         a.locator = copyLocator(locator,{});
65242                                                 }
65243                                                 //}catch(e){console.error('@@@@@'+e)}
65244                                                 domBuilder.locator = locator2;
65245                                                 if(appendElement(el,domBuilder,currentNSMap)){
65246                                                         parseStack.push(el);
65247                                                 }
65248                                                 domBuilder.locator = locator;
65249                                         }else {
65250                                                 if(appendElement(el,domBuilder,currentNSMap)){
65251                                                         parseStack.push(el);
65252                                                 }
65253                                         }
65254                                         
65255                                         
65256                                         
65257                                         if(el.uri === 'http://www.w3.org/1999/xhtml' && !el.closed){
65258                                                 end = parseHtmlSpecialContent(source,end,el.tagName,entityReplacer,domBuilder);
65259                                         }else {
65260                                                 end++;
65261                                         }
65262                                 }
65263                         }catch(e){
65264                                 errorHandler.error('element parse error: '+e);
65265                                 //errorHandler.error('element parse error: '+e);
65266                                 end = -1;
65267                                 //throw e;
65268                         }
65269                         if(end>start){
65270                                 start = end;
65271                         }else {
65272                                 //TODO: 这里有可能sax回退,有位置错误风险
65273                                 appendText(Math.max(tagStart,start)+1);
65274                         }
65275                 }
65276         }
65277         function copyLocator(f,t){
65278                 t.lineNumber = f.lineNumber;
65279                 t.columnNumber = f.columnNumber;
65280                 return t;
65281         }
65282
65283         /**
65284          * @see #appendElement(source,elStartEnd,el,selfClosed,entityReplacer,domBuilder,parseStack);
65285          * @return end of the elementStartPart(end of elementEndPart for selfClosed el)
65286          */
65287         function parseElementStartPart(source,start,el,currentNSMap,entityReplacer,errorHandler){
65288                 var attrName;
65289                 var value;
65290                 var p = ++start;
65291                 var s = S_TAG;//status
65292                 while(true){
65293                         var c = source.charAt(p);
65294                         switch(c){
65295                         case '=':
65296                                 if(s === S_ATTR){//attrName
65297                                         attrName = source.slice(start,p);
65298                                         s = S_EQ;
65299                                 }else if(s === S_ATTR_SPACE){
65300                                         s = S_EQ;
65301                                 }else {
65302                                         //fatalError: equal must after attrName or space after attrName
65303                                         throw new Error('attribute equal must after attrName');
65304                                 }
65305                                 break;
65306                         case '\'':
65307                         case '"':
65308                                 if(s === S_EQ || s === S_ATTR //|| s == S_ATTR_SPACE
65309                                         ){//equal
65310                                         if(s === S_ATTR){
65311                                                 errorHandler.warning('attribute value must after "="');
65312                                                 attrName = source.slice(start,p);
65313                                         }
65314                                         start = p+1;
65315                                         p = source.indexOf(c,start);
65316                                         if(p>0){
65317                                                 value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65318                                                 el.add(attrName,value,start-1);
65319                                                 s = S_ATTR_END;
65320                                         }else {
65321                                                 //fatalError: no end quot match
65322                                                 throw new Error('attribute value no end \''+c+'\' match');
65323                                         }
65324                                 }else if(s == S_ATTR_NOQUOT_VALUE){
65325                                         value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65326                                         //console.log(attrName,value,start,p)
65327                                         el.add(attrName,value,start);
65328                                         //console.dir(el)
65329                                         errorHandler.warning('attribute "'+attrName+'" missed start quot('+c+')!!');
65330                                         start = p+1;
65331                                         s = S_ATTR_END;
65332                                 }else {
65333                                         //fatalError: no equal before
65334                                         throw new Error('attribute value must after "="');
65335                                 }
65336                                 break;
65337                         case '/':
65338                                 switch(s){
65339                                 case S_TAG:
65340                                         el.setTagName(source.slice(start,p));
65341                                 case S_ATTR_END:
65342                                 case S_TAG_SPACE:
65343                                 case S_TAG_CLOSE:
65344                                         s =S_TAG_CLOSE;
65345                                         el.closed = true;
65346                                 case S_ATTR_NOQUOT_VALUE:
65347                                 case S_ATTR:
65348                                 case S_ATTR_SPACE:
65349                                         break;
65350                                 //case S_EQ:
65351                                 default:
65352                                         throw new Error("attribute invalid close char('/')")
65353                                 }
65354                                 break;
65355                         case ''://end document
65356                                 //throw new Error('unexpected end of input')
65357                                 errorHandler.error('unexpected end of input');
65358                                 if(s == S_TAG){
65359                                         el.setTagName(source.slice(start,p));
65360                                 }
65361                                 return p;
65362                         case '>':
65363                                 switch(s){
65364                                 case S_TAG:
65365                                         el.setTagName(source.slice(start,p));
65366                                 case S_ATTR_END:
65367                                 case S_TAG_SPACE:
65368                                 case S_TAG_CLOSE:
65369                                         break;//normal
65370                                 case S_ATTR_NOQUOT_VALUE://Compatible state
65371                                 case S_ATTR:
65372                                         value = source.slice(start,p);
65373                                         if(value.slice(-1) === '/'){
65374                                                 el.closed  = true;
65375                                                 value = value.slice(0,-1);
65376                                         }
65377                                 case S_ATTR_SPACE:
65378                                         if(s === S_ATTR_SPACE){
65379                                                 value = attrName;
65380                                         }
65381                                         if(s == S_ATTR_NOQUOT_VALUE){
65382                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65383                                                 el.add(attrName,value.replace(/&#?\w+;/g,entityReplacer),start);
65384                                         }else {
65385                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !value.match(/^(?:disabled|checked|selected)$/i)){
65386                                                         errorHandler.warning('attribute "'+value+'" missed value!! "'+value+'" instead!!');
65387                                                 }
65388                                                 el.add(value,value,start);
65389                                         }
65390                                         break;
65391                                 case S_EQ:
65392                                         throw new Error('attribute value missed!!');
65393                                 }
65394         //                      console.log(tagName,tagNamePattern,tagNamePattern.test(tagName))
65395                                 return p;
65396                         /*xml space '\x20' | #x9 | #xD | #xA; */
65397                         case '\u0080':
65398                                 c = ' ';
65399                         default:
65400                                 if(c<= ' '){//space
65401                                         switch(s){
65402                                         case S_TAG:
65403                                                 el.setTagName(source.slice(start,p));//tagName
65404                                                 s = S_TAG_SPACE;
65405                                                 break;
65406                                         case S_ATTR:
65407                                                 attrName = source.slice(start,p);
65408                                                 s = S_ATTR_SPACE;
65409                                                 break;
65410                                         case S_ATTR_NOQUOT_VALUE:
65411                                                 var value = source.slice(start,p).replace(/&#?\w+;/g,entityReplacer);
65412                                                 errorHandler.warning('attribute "'+value+'" missed quot(")!!');
65413                                                 el.add(attrName,value,start);
65414                                         case S_ATTR_END:
65415                                                 s = S_TAG_SPACE;
65416                                                 break;
65417                                         //case S_TAG_SPACE:
65418                                         //case S_EQ:
65419                                         //case S_ATTR_SPACE:
65420                                         //      void();break;
65421                                         //case S_TAG_CLOSE:
65422                                                 //ignore warning
65423                                         }
65424                                 }else {//not space
65425         //S_TAG,        S_ATTR, S_EQ,   S_ATTR_NOQUOT_VALUE
65426         //S_ATTR_SPACE, S_ATTR_END,     S_TAG_SPACE, S_TAG_CLOSE
65427                                         switch(s){
65428                                         //case S_TAG:void();break;
65429                                         //case S_ATTR:void();break;
65430                                         //case S_ATTR_NOQUOT_VALUE:void();break;
65431                                         case S_ATTR_SPACE:
65432                                                 var tagName =  el.tagName;
65433                                                 if(currentNSMap[''] !== 'http://www.w3.org/1999/xhtml' || !attrName.match(/^(?:disabled|checked|selected)$/i)){
65434                                                         errorHandler.warning('attribute "'+attrName+'" missed value!! "'+attrName+'" instead2!!');
65435                                                 }
65436                                                 el.add(attrName,attrName,start);
65437                                                 start = p;
65438                                                 s = S_ATTR;
65439                                                 break;
65440                                         case S_ATTR_END:
65441                                                 errorHandler.warning('attribute space is required"'+attrName+'"!!');
65442                                         case S_TAG_SPACE:
65443                                                 s = S_ATTR;
65444                                                 start = p;
65445                                                 break;
65446                                         case S_EQ:
65447                                                 s = S_ATTR_NOQUOT_VALUE;
65448                                                 start = p;
65449                                                 break;
65450                                         case S_TAG_CLOSE:
65451                                                 throw new Error("elements closed character '/' and '>' must be connected to");
65452                                         }
65453                                 }
65454                         }//end outer switch
65455                         //console.log('p++',p)
65456                         p++;
65457                 }
65458         }
65459         /**
65460          * @return true if has new namespace define
65461          */
65462         function appendElement(el,domBuilder,currentNSMap){
65463                 var tagName = el.tagName;
65464                 var localNSMap = null;
65465                 //var currentNSMap = parseStack[parseStack.length-1].currentNSMap;
65466                 var i = el.length;
65467                 while(i--){
65468                         var a = el[i];
65469                         var qName = a.qName;
65470                         var value = a.value;
65471                         var nsp = qName.indexOf(':');
65472                         if(nsp>0){
65473                                 var prefix = a.prefix = qName.slice(0,nsp);
65474                                 var localName = qName.slice(nsp+1);
65475                                 var nsPrefix = prefix === 'xmlns' && localName;
65476                         }else {
65477                                 localName = qName;
65478                                 prefix = null;
65479                                 nsPrefix = qName === 'xmlns' && '';
65480                         }
65481                         //can not set prefix,because prefix !== ''
65482                         a.localName = localName ;
65483                         //prefix == null for no ns prefix attribute 
65484                         if(nsPrefix !== false){//hack!!
65485                                 if(localNSMap == null){
65486                                         localNSMap = {};
65487                                         //console.log(currentNSMap,0)
65488                                         _copy(currentNSMap,currentNSMap={});
65489                                         //console.log(currentNSMap,1)
65490                                 }
65491                                 currentNSMap[nsPrefix] = localNSMap[nsPrefix] = value;
65492                                 a.uri = 'http://www.w3.org/2000/xmlns/';
65493                                 domBuilder.startPrefixMapping(nsPrefix, value); 
65494                         }
65495                 }
65496                 var i = el.length;
65497                 while(i--){
65498                         a = el[i];
65499                         var prefix = a.prefix;
65500                         if(prefix){//no prefix attribute has no namespace
65501                                 if(prefix === 'xml'){
65502                                         a.uri = 'http://www.w3.org/XML/1998/namespace';
65503                                 }if(prefix !== 'xmlns'){
65504                                         a.uri = currentNSMap[prefix || ''];
65505                                         
65506                                         //{console.log('###'+a.qName,domBuilder.locator.systemId+'',currentNSMap,a.uri)}
65507                                 }
65508                         }
65509                 }
65510                 var nsp = tagName.indexOf(':');
65511                 if(nsp>0){
65512                         prefix = el.prefix = tagName.slice(0,nsp);
65513                         localName = el.localName = tagName.slice(nsp+1);
65514                 }else {
65515                         prefix = null;//important!!
65516                         localName = el.localName = tagName;
65517                 }
65518                 //no prefix element has default namespace
65519                 var ns = el.uri = currentNSMap[prefix || ''];
65520                 domBuilder.startElement(ns,localName,tagName,el);
65521                 //endPrefixMapping and startPrefixMapping have not any help for dom builder
65522                 //localNSMap = null
65523                 if(el.closed){
65524                         domBuilder.endElement(ns,localName,tagName);
65525                         if(localNSMap){
65526                                 for(prefix in localNSMap){
65527                                         domBuilder.endPrefixMapping(prefix); 
65528                                 }
65529                         }
65530                 }else {
65531                         el.currentNSMap = currentNSMap;
65532                         el.localNSMap = localNSMap;
65533                         //parseStack.push(el);
65534                         return true;
65535                 }
65536         }
65537         function parseHtmlSpecialContent(source,elStartEnd,tagName,entityReplacer,domBuilder){
65538                 if(/^(?:script|textarea)$/i.test(tagName)){
65539                         var elEndStart =  source.indexOf('</'+tagName+'>',elStartEnd);
65540                         var text = source.substring(elStartEnd+1,elEndStart);
65541                         if(/[&<]/.test(text)){
65542                                 if(/^script$/i.test(tagName)){
65543                                         //if(!/\]\]>/.test(text)){
65544                                                 //lexHandler.startCDATA();
65545                                                 domBuilder.characters(text,0,text.length);
65546                                                 //lexHandler.endCDATA();
65547                                                 return elEndStart;
65548                                         //}
65549                                 }//}else{//text area
65550                                         text = text.replace(/&#?\w+;/g,entityReplacer);
65551                                         domBuilder.characters(text,0,text.length);
65552                                         return elEndStart;
65553                                 //}
65554                                 
65555                         }
65556                 }
65557                 return elStartEnd+1;
65558         }
65559         function fixSelfClosed(source,elStartEnd,tagName,closeMap){
65560                 //if(tagName in closeMap){
65561                 var pos = closeMap[tagName];
65562                 if(pos == null){
65563                         //console.log(tagName)
65564                         pos =  source.lastIndexOf('</'+tagName+'>');
65565                         if(pos<elStartEnd){//忘记闭合
65566                                 pos = source.lastIndexOf('</'+tagName);
65567                         }
65568                         closeMap[tagName] =pos;
65569                 }
65570                 return pos<elStartEnd;
65571                 //} 
65572         }
65573         function _copy(source,target){
65574                 for(var n in source){target[n] = source[n];}
65575         }
65576         function parseDCC(source,start,domBuilder,errorHandler){//sure start with '<!'
65577                 var next= source.charAt(start+2);
65578                 switch(next){
65579                 case '-':
65580                         if(source.charAt(start + 3) === '-'){
65581                                 var end = source.indexOf('-->',start+4);
65582                                 //append comment source.substring(4,end)//<!--
65583                                 if(end>start){
65584                                         domBuilder.comment(source,start+4,end-start-4);
65585                                         return end+3;
65586                                 }else {
65587                                         errorHandler.error("Unclosed comment");
65588                                         return -1;
65589                                 }
65590                         }else {
65591                                 //error
65592                                 return -1;
65593                         }
65594                 default:
65595                         if(source.substr(start+3,6) == 'CDATA['){
65596                                 var end = source.indexOf(']]>',start+9);
65597                                 domBuilder.startCDATA();
65598                                 domBuilder.characters(source,start+9,end-start-9);
65599                                 domBuilder.endCDATA(); 
65600                                 return end+3;
65601                         }
65602                         //<!DOCTYPE
65603                         //startDTD(java.lang.String name, java.lang.String publicId, java.lang.String systemId) 
65604                         var matchs = split(source,start);
65605                         var len = matchs.length;
65606                         if(len>1 && /!doctype/i.test(matchs[0][0])){
65607                                 var name = matchs[1][0];
65608                                 var pubid = len>3 && /^public$/i.test(matchs[2][0]) && matchs[3][0];
65609                                 var sysid = len>4 && matchs[4][0];
65610                                 var lastMatch = matchs[len-1];
65611                                 domBuilder.startDTD(name,pubid && pubid.replace(/^(['"])(.*?)\1$/,'$2'),
65612                                                 sysid && sysid.replace(/^(['"])(.*?)\1$/,'$2'));
65613                                 domBuilder.endDTD();
65614                                 
65615                                 return lastMatch.index+lastMatch[0].length
65616                         }
65617                 }
65618                 return -1;
65619         }
65620
65621
65622
65623         function parseInstruction(source,start,domBuilder){
65624                 var end = source.indexOf('?>',start);
65625                 if(end){
65626                         var match = source.substring(start,end).match(/^<\?(\S*)\s*([\s\S]*?)\s*$/);
65627                         if(match){
65628                                 var len = match[0].length;
65629                                 domBuilder.processingInstruction(match[1], match[2]) ;
65630                                 return end+2;
65631                         }else {//error
65632                                 return -1;
65633                         }
65634                 }
65635                 return -1;
65636         }
65637
65638         /**
65639          * @param source
65640          */
65641         function ElementAttributes(source){
65642                 
65643         }
65644         ElementAttributes.prototype = {
65645                 setTagName:function(tagName){
65646                         if(!tagNamePattern.test(tagName)){
65647                                 throw new Error('invalid tagName:'+tagName)
65648                         }
65649                         this.tagName = tagName;
65650                 },
65651                 add:function(qName,value,offset){
65652                         if(!tagNamePattern.test(qName)){
65653                                 throw new Error('invalid attribute:'+qName)
65654                         }
65655                         this[this.length++] = {qName:qName,value:value,offset:offset};
65656                 },
65657                 length:0,
65658                 getLocalName:function(i){return this[i].localName},
65659                 getLocator:function(i){return this[i].locator},
65660                 getQName:function(i){return this[i].qName},
65661                 getURI:function(i){return this[i].uri},
65662                 getValue:function(i){return this[i].value}
65663         //      ,getIndex:function(uri, localName)){
65664         //              if(localName){
65665         //                      
65666         //              }else{
65667         //                      var qName = uri
65668         //              }
65669         //      },
65670         //      getValue:function(){return this.getValue(this.getIndex.apply(this,arguments))},
65671         //      getType:function(uri,localName){}
65672         //      getType:function(i){},
65673         };
65674
65675
65676
65677
65678         function _set_proto_(thiz,parent){
65679                 thiz.__proto__ = parent;
65680                 return thiz;
65681         }
65682         if(!(_set_proto_({},_set_proto_.prototype) instanceof _set_proto_)){
65683                 _set_proto_ = function(thiz,parent){
65684                         function p(){}          p.prototype = parent;
65685                         p = new p();
65686                         for(parent in thiz){
65687                                 p[parent] = thiz[parent];
65688                         }
65689                         return p;
65690                 };
65691         }
65692
65693         function split(source,start){
65694                 var match;
65695                 var buf = [];
65696                 var reg = /'[^']+'|"[^"]+"|[^\s<>\/=]+=?|(\/?\s*>|<)/g;
65697                 reg.lastIndex = start;
65698                 reg.exec(source);//skip <
65699                 while(match = reg.exec(source)){
65700                         buf.push(match);
65701                         if(match[1]){ return buf; }
65702                 }
65703         }
65704
65705         var XMLReader_1 = XMLReader;
65706
65707         var sax = {
65708                 XMLReader: XMLReader_1
65709         };
65710
65711         /*
65712          * DOM Level 2
65713          * Object DOMException
65714          * @see http://www.w3.org/TR/REC-DOM-Level-1/ecma-script-language-binding.html
65715          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/ecma-script-binding.html
65716          */
65717
65718         function copy$2(src,dest){
65719                 for(var p in src){
65720                         dest[p] = src[p];
65721                 }
65722         }
65723         /**
65724         ^\w+\.prototype\.([_\w]+)\s*=\s*((?:.*\{\s*?[\r\n][\s\S]*?^})|\S.*?(?=[;\r\n]));?
65725         ^\w+\.prototype\.([_\w]+)\s*=\s*(\S.*?(?=[;\r\n]));?
65726          */
65727         function _extends(Class,Super){
65728                 var pt = Class.prototype;
65729                 if(Object.create){
65730                         var ppt = Object.create(Super.prototype);
65731                         pt.__proto__ = ppt;
65732                 }
65733                 if(!(pt instanceof Super)){
65734                         function t(){}          t.prototype = Super.prototype;
65735                         t = new t();
65736                         copy$2(pt,t);
65737                         Class.prototype = pt = t;
65738                 }
65739                 if(pt.constructor != Class){
65740                         if(typeof Class != 'function'){
65741                                 console.error("unknow Class:"+Class);
65742                         }
65743                         pt.constructor = Class;
65744                 }
65745         }
65746         var htmlns = 'http://www.w3.org/1999/xhtml' ;
65747         // Node Types
65748         var NodeType = {};
65749         var ELEMENT_NODE                = NodeType.ELEMENT_NODE                = 1;
65750         var ATTRIBUTE_NODE              = NodeType.ATTRIBUTE_NODE              = 2;
65751         var TEXT_NODE                   = NodeType.TEXT_NODE                   = 3;
65752         var CDATA_SECTION_NODE          = NodeType.CDATA_SECTION_NODE          = 4;
65753         var ENTITY_REFERENCE_NODE       = NodeType.ENTITY_REFERENCE_NODE       = 5;
65754         var ENTITY_NODE                 = NodeType.ENTITY_NODE                 = 6;
65755         var PROCESSING_INSTRUCTION_NODE = NodeType.PROCESSING_INSTRUCTION_NODE = 7;
65756         var COMMENT_NODE                = NodeType.COMMENT_NODE                = 8;
65757         var DOCUMENT_NODE               = NodeType.DOCUMENT_NODE               = 9;
65758         var DOCUMENT_TYPE_NODE          = NodeType.DOCUMENT_TYPE_NODE          = 10;
65759         var DOCUMENT_FRAGMENT_NODE      = NodeType.DOCUMENT_FRAGMENT_NODE      = 11;
65760         var NOTATION_NODE               = NodeType.NOTATION_NODE               = 12;
65761
65762         // ExceptionCode
65763         var ExceptionCode = {};
65764         var ExceptionMessage = {};
65765         var INDEX_SIZE_ERR              = ExceptionCode.INDEX_SIZE_ERR              = ((ExceptionMessage[1]="Index size error"),1);
65766         var DOMSTRING_SIZE_ERR          = ExceptionCode.DOMSTRING_SIZE_ERR          = ((ExceptionMessage[2]="DOMString size error"),2);
65767         var HIERARCHY_REQUEST_ERR       = ExceptionCode.HIERARCHY_REQUEST_ERR       = ((ExceptionMessage[3]="Hierarchy request error"),3);
65768         var WRONG_DOCUMENT_ERR          = ExceptionCode.WRONG_DOCUMENT_ERR          = ((ExceptionMessage[4]="Wrong document"),4);
65769         var INVALID_CHARACTER_ERR       = ExceptionCode.INVALID_CHARACTER_ERR       = ((ExceptionMessage[5]="Invalid character"),5);
65770         var NO_DATA_ALLOWED_ERR         = ExceptionCode.NO_DATA_ALLOWED_ERR         = ((ExceptionMessage[6]="No data allowed"),6);
65771         var NO_MODIFICATION_ALLOWED_ERR = ExceptionCode.NO_MODIFICATION_ALLOWED_ERR = ((ExceptionMessage[7]="No modification allowed"),7);
65772         var NOT_FOUND_ERR               = ExceptionCode.NOT_FOUND_ERR               = ((ExceptionMessage[8]="Not found"),8);
65773         var NOT_SUPPORTED_ERR           = ExceptionCode.NOT_SUPPORTED_ERR           = ((ExceptionMessage[9]="Not supported"),9);
65774         var INUSE_ATTRIBUTE_ERR         = ExceptionCode.INUSE_ATTRIBUTE_ERR         = ((ExceptionMessage[10]="Attribute in use"),10);
65775         //level2
65776         var INVALID_STATE_ERR           = ExceptionCode.INVALID_STATE_ERR               = ((ExceptionMessage[11]="Invalid state"),11);
65777         var SYNTAX_ERR                  = ExceptionCode.SYNTAX_ERR                      = ((ExceptionMessage[12]="Syntax error"),12);
65778         var INVALID_MODIFICATION_ERR    = ExceptionCode.INVALID_MODIFICATION_ERR        = ((ExceptionMessage[13]="Invalid modification"),13);
65779         var NAMESPACE_ERR               = ExceptionCode.NAMESPACE_ERR                   = ((ExceptionMessage[14]="Invalid namespace"),14);
65780         var INVALID_ACCESS_ERR          = ExceptionCode.INVALID_ACCESS_ERR              = ((ExceptionMessage[15]="Invalid access"),15);
65781
65782
65783         function DOMException$2(code, message) {
65784                 if(message instanceof Error){
65785                         var error = message;
65786                 }else {
65787                         error = this;
65788                         Error.call(this, ExceptionMessage[code]);
65789                         this.message = ExceptionMessage[code];
65790                         if(Error.captureStackTrace) { Error.captureStackTrace(this, DOMException$2); }
65791                 }
65792                 error.code = code;
65793                 if(message) { this.message = this.message + ": " + message; }
65794                 return error;
65795         }DOMException$2.prototype = Error.prototype;
65796         copy$2(ExceptionCode,DOMException$2);
65797         /**
65798          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
65799          * 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.
65800          * The items in the NodeList are accessible via an integral index, starting from 0.
65801          */
65802         function NodeList() {
65803         }NodeList.prototype = {
65804                 /**
65805                  * The number of nodes in the list. The range of valid child node indices is 0 to length-1 inclusive.
65806                  * @standard level1
65807                  */
65808                 length:0, 
65809                 /**
65810                  * 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.
65811                  * @standard level1
65812                  * @param index  unsigned long 
65813                  *   Index into the collection.
65814                  * @return Node
65815                  *      The node at the indexth position in the NodeList, or null if that is not a valid index. 
65816                  */
65817                 item: function(index) {
65818                         return this[index] || null;
65819                 },
65820                 toString:function(isHTML,nodeFilter){
65821                         for(var buf = [], i = 0;i<this.length;i++){
65822                                 serializeToString(this[i],buf,isHTML,nodeFilter);
65823                         }
65824                         return buf.join('');
65825                 }
65826         };
65827         function LiveNodeList(node,refresh){
65828                 this._node = node;
65829                 this._refresh = refresh;
65830                 _updateLiveList(this);
65831         }
65832         function _updateLiveList(list){
65833                 var inc = list._node._inc || list._node.ownerDocument._inc;
65834                 if(list._inc != inc){
65835                         var ls = list._refresh(list._node);
65836                         //console.log(ls.length)
65837                         __set__(list,'length',ls.length);
65838                         copy$2(ls,list);
65839                         list._inc = inc;
65840                 }
65841         }
65842         LiveNodeList.prototype.item = function(i){
65843                 _updateLiveList(this);
65844                 return this[i];
65845         };
65846
65847         _extends(LiveNodeList,NodeList);
65848         /**
65849          * 
65850          * 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.
65851          * NamedNodeMap objects in the DOM are live.
65852          * used for attributes or DocumentType entities 
65853          */
65854         function NamedNodeMap() {
65855         }
65856         function _findNodeIndex(list,node){
65857                 var i = list.length;
65858                 while(i--){
65859                         if(list[i] === node){return i}
65860                 }
65861         }
65862
65863         function _addNamedNode(el,list,newAttr,oldAttr){
65864                 if(oldAttr){
65865                         list[_findNodeIndex(list,oldAttr)] = newAttr;
65866                 }else {
65867                         list[list.length++] = newAttr;
65868                 }
65869                 if(el){
65870                         newAttr.ownerElement = el;
65871                         var doc = el.ownerDocument;
65872                         if(doc){
65873                                 oldAttr && _onRemoveAttribute(doc,el,oldAttr);
65874                                 _onAddAttribute(doc,el,newAttr);
65875                         }
65876                 }
65877         }
65878         function _removeNamedNode(el,list,attr){
65879                 //console.log('remove attr:'+attr)
65880                 var i = _findNodeIndex(list,attr);
65881                 if(i>=0){
65882                         var lastIndex = list.length-1;
65883                         while(i<lastIndex){
65884                                 list[i] = list[++i];
65885                         }
65886                         list.length = lastIndex;
65887                         if(el){
65888                                 var doc = el.ownerDocument;
65889                                 if(doc){
65890                                         _onRemoveAttribute(doc,el,attr);
65891                                         attr.ownerElement = null;
65892                                 }
65893                         }
65894                 }else {
65895                         throw DOMException$2(NOT_FOUND_ERR,new Error(el.tagName+'@'+attr))
65896                 }
65897         }
65898         NamedNodeMap.prototype = {
65899                 length:0,
65900                 item:NodeList.prototype.item,
65901                 getNamedItem: function(key) {
65902         //              if(key.indexOf(':')>0 || key == 'xmlns'){
65903         //                      return null;
65904         //              }
65905                         //console.log()
65906                         var i = this.length;
65907                         while(i--){
65908                                 var attr = this[i];
65909                                 //console.log(attr.nodeName,key)
65910                                 if(attr.nodeName == key){
65911                                         return attr;
65912                                 }
65913                         }
65914                 },
65915                 setNamedItem: function(attr) {
65916                         var el = attr.ownerElement;
65917                         if(el && el!=this._ownerElement){
65918                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65919                         }
65920                         var oldAttr = this.getNamedItem(attr.nodeName);
65921                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65922                         return oldAttr;
65923                 },
65924                 /* returns Node */
65925                 setNamedItemNS: function(attr) {// raises: WRONG_DOCUMENT_ERR,NO_MODIFICATION_ALLOWED_ERR,INUSE_ATTRIBUTE_ERR
65926                         var el = attr.ownerElement, oldAttr;
65927                         if(el && el!=this._ownerElement){
65928                                 throw new DOMException$2(INUSE_ATTRIBUTE_ERR);
65929                         }
65930                         oldAttr = this.getNamedItemNS(attr.namespaceURI,attr.localName);
65931                         _addNamedNode(this._ownerElement,this,attr,oldAttr);
65932                         return oldAttr;
65933                 },
65934
65935                 /* returns Node */
65936                 removeNamedItem: function(key) {
65937                         var attr = this.getNamedItem(key);
65938                         _removeNamedNode(this._ownerElement,this,attr);
65939                         return attr;
65940                         
65941                         
65942                 },// raises: NOT_FOUND_ERR,NO_MODIFICATION_ALLOWED_ERR
65943                 
65944                 //for level2
65945                 removeNamedItemNS:function(namespaceURI,localName){
65946                         var attr = this.getNamedItemNS(namespaceURI,localName);
65947                         _removeNamedNode(this._ownerElement,this,attr);
65948                         return attr;
65949                 },
65950                 getNamedItemNS: function(namespaceURI, localName) {
65951                         var i = this.length;
65952                         while(i--){
65953                                 var node = this[i];
65954                                 if(node.localName == localName && node.namespaceURI == namespaceURI){
65955                                         return node;
65956                                 }
65957                         }
65958                         return null;
65959                 }
65960         };
65961         /**
65962          * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490
65963          */
65964         function DOMImplementation(/* Object */ features) {
65965                 this._features = {};
65966                 if (features) {
65967                         for (var feature in features) {
65968                                  this._features = features[feature];
65969                         }
65970                 }
65971         }
65972         DOMImplementation.prototype = {
65973                 hasFeature: function(/* string */ feature, /* string */ version) {
65974                         var versions = this._features[feature.toLowerCase()];
65975                         if (versions && (!version || version in versions)) {
65976                                 return true;
65977                         } else {
65978                                 return false;
65979                         }
65980                 },
65981                 // Introduced in DOM Level 2:
65982                 createDocument:function(namespaceURI,  qualifiedName, doctype){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR,WRONG_DOCUMENT_ERR
65983                         var doc = new Document();
65984                         doc.implementation = this;
65985                         doc.childNodes = new NodeList();
65986                         doc.doctype = doctype;
65987                         if(doctype){
65988                                 doc.appendChild(doctype);
65989                         }
65990                         if(qualifiedName){
65991                                 var root = doc.createElementNS(namespaceURI,qualifiedName);
65992                                 doc.appendChild(root);
65993                         }
65994                         return doc;
65995                 },
65996                 // Introduced in DOM Level 2:
65997                 createDocumentType:function(qualifiedName, publicId, systemId){// raises:INVALID_CHARACTER_ERR,NAMESPACE_ERR
65998                         var node = new DocumentType();
65999                         node.name = qualifiedName;
66000                         node.nodeName = qualifiedName;
66001                         node.publicId = publicId;
66002                         node.systemId = systemId;
66003                         // Introduced in DOM Level 2:
66004                         //readonly attribute DOMString        internalSubset;
66005                         
66006                         //TODO:..
66007                         //  readonly attribute NamedNodeMap     entities;
66008                         //  readonly attribute NamedNodeMap     notations;
66009                         return node;
66010                 }
66011         };
66012
66013
66014         /**
66015          * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
66016          */
66017
66018         function Node() {
66019         }
66020         Node.prototype = {
66021                 firstChild : null,
66022                 lastChild : null,
66023                 previousSibling : null,
66024                 nextSibling : null,
66025                 attributes : null,
66026                 parentNode : null,
66027                 childNodes : null,
66028                 ownerDocument : null,
66029                 nodeValue : null,
66030                 namespaceURI : null,
66031                 prefix : null,
66032                 localName : null,
66033                 // Modified in DOM Level 2:
66034                 insertBefore:function(newChild, refChild){//raises 
66035                         return _insertBefore(this,newChild,refChild);
66036                 },
66037                 replaceChild:function(newChild, oldChild){//raises 
66038                         this.insertBefore(newChild,oldChild);
66039                         if(oldChild){
66040                                 this.removeChild(oldChild);
66041                         }
66042                 },
66043                 removeChild:function(oldChild){
66044                         return _removeChild(this,oldChild);
66045                 },
66046                 appendChild:function(newChild){
66047                         return this.insertBefore(newChild,null);
66048                 },
66049                 hasChildNodes:function(){
66050                         return this.firstChild != null;
66051                 },
66052                 cloneNode:function(deep){
66053                         return cloneNode(this.ownerDocument||this,this,deep);
66054                 },
66055                 // Modified in DOM Level 2:
66056                 normalize:function(){
66057                         var child = this.firstChild;
66058                         while(child){
66059                                 var next = child.nextSibling;
66060                                 if(next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE){
66061                                         this.removeChild(next);
66062                                         child.appendData(next.data);
66063                                 }else {
66064                                         child.normalize();
66065                                         child = next;
66066                                 }
66067                         }
66068                 },
66069                 // Introduced in DOM Level 2:
66070                 isSupported:function(feature, version){
66071                         return this.ownerDocument.implementation.hasFeature(feature,version);
66072                 },
66073             // Introduced in DOM Level 2:
66074             hasAttributes:function(){
66075                 return this.attributes.length>0;
66076             },
66077             lookupPrefix:function(namespaceURI){
66078                 var el = this;
66079                 while(el){
66080                         var map = el._nsMap;
66081                         //console.dir(map)
66082                         if(map){
66083                                 for(var n in map){
66084                                         if(map[n] == namespaceURI){
66085                                                 return n;
66086                                         }
66087                                 }
66088                         }
66089                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66090                 }
66091                 return null;
66092             },
66093             // Introduced in DOM Level 3:
66094             lookupNamespaceURI:function(prefix){
66095                 var el = this;
66096                 while(el){
66097                         var map = el._nsMap;
66098                         //console.dir(map)
66099                         if(map){
66100                                 if(prefix in map){
66101                                         return map[prefix] ;
66102                                 }
66103                         }
66104                         el = el.nodeType == ATTRIBUTE_NODE?el.ownerDocument : el.parentNode;
66105                 }
66106                 return null;
66107             },
66108             // Introduced in DOM Level 3:
66109             isDefaultNamespace:function(namespaceURI){
66110                 var prefix = this.lookupPrefix(namespaceURI);
66111                 return prefix == null;
66112             }
66113         };
66114
66115
66116         function _xmlEncoder(c){
66117                 return c == '<' && '&lt;' ||
66118                  c == '>' && '&gt;' ||
66119                  c == '&' && '&amp;' ||
66120                  c == '"' && '&quot;' ||
66121                  '&#'+c.charCodeAt()+';'
66122         }
66123
66124
66125         copy$2(NodeType,Node);
66126         copy$2(NodeType,Node.prototype);
66127
66128         /**
66129          * @param callback return true for continue,false for break
66130          * @return boolean true: break visit;
66131          */
66132         function _visitNode(node,callback){
66133                 if(callback(node)){
66134                         return true;
66135                 }
66136                 if(node = node.firstChild){
66137                         do{
66138                                 if(_visitNode(node,callback)){return true}
66139                 }while(node=node.nextSibling)
66140             }
66141         }
66142
66143
66144
66145         function Document(){
66146         }
66147         function _onAddAttribute(doc,el,newAttr){
66148                 doc && doc._inc++;
66149                 var ns = newAttr.namespaceURI ;
66150                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66151                         //update namespace
66152                         el._nsMap[newAttr.prefix?newAttr.localName:''] = newAttr.value;
66153                 }
66154         }
66155         function _onRemoveAttribute(doc,el,newAttr,remove){
66156                 doc && doc._inc++;
66157                 var ns = newAttr.namespaceURI ;
66158                 if(ns == 'http://www.w3.org/2000/xmlns/'){
66159                         //update namespace
66160                         delete el._nsMap[newAttr.prefix?newAttr.localName:''];
66161                 }
66162         }
66163         function _onUpdateChild(doc,el,newChild){
66164                 if(doc && doc._inc){
66165                         doc._inc++;
66166                         //update childNodes
66167                         var cs = el.childNodes;
66168                         if(newChild){
66169                                 cs[cs.length++] = newChild;
66170                         }else {
66171                                 //console.log(1)
66172                                 var child = el.firstChild;
66173                                 var i = 0;
66174                                 while(child){
66175                                         cs[i++] = child;
66176                                         child =child.nextSibling;
66177                                 }
66178                                 cs.length = i;
66179                         }
66180                 }
66181         }
66182
66183         /**
66184          * attributes;
66185          * children;
66186          * 
66187          * writeable properties:
66188          * nodeValue,Attr:value,CharacterData:data
66189          * prefix
66190          */
66191         function _removeChild(parentNode,child){
66192                 var previous = child.previousSibling;
66193                 var next = child.nextSibling;
66194                 if(previous){
66195                         previous.nextSibling = next;
66196                 }else {
66197                         parentNode.firstChild = next;
66198                 }
66199                 if(next){
66200                         next.previousSibling = previous;
66201                 }else {
66202                         parentNode.lastChild = previous;
66203                 }
66204                 _onUpdateChild(parentNode.ownerDocument,parentNode);
66205                 return child;
66206         }
66207         /**
66208          * preformance key(refChild == null)
66209          */
66210         function _insertBefore(parentNode,newChild,nextChild){
66211                 var cp = newChild.parentNode;
66212                 if(cp){
66213                         cp.removeChild(newChild);//remove and update
66214                 }
66215                 if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66216                         var newFirst = newChild.firstChild;
66217                         if (newFirst == null) {
66218                                 return newChild;
66219                         }
66220                         var newLast = newChild.lastChild;
66221                 }else {
66222                         newFirst = newLast = newChild;
66223                 }
66224                 var pre = nextChild ? nextChild.previousSibling : parentNode.lastChild;
66225
66226                 newFirst.previousSibling = pre;
66227                 newLast.nextSibling = nextChild;
66228                 
66229                 
66230                 if(pre){
66231                         pre.nextSibling = newFirst;
66232                 }else {
66233                         parentNode.firstChild = newFirst;
66234                 }
66235                 if(nextChild == null){
66236                         parentNode.lastChild = newLast;
66237                 }else {
66238                         nextChild.previousSibling = newLast;
66239                 }
66240                 do{
66241                         newFirst.parentNode = parentNode;
66242                 }while(newFirst !== newLast && (newFirst= newFirst.nextSibling))
66243                 _onUpdateChild(parentNode.ownerDocument||parentNode,parentNode);
66244                 //console.log(parentNode.lastChild.nextSibling == null)
66245                 if (newChild.nodeType == DOCUMENT_FRAGMENT_NODE) {
66246                         newChild.firstChild = newChild.lastChild = null;
66247                 }
66248                 return newChild;
66249         }
66250         function _appendSingleChild(parentNode,newChild){
66251                 var cp = newChild.parentNode;
66252                 if(cp){
66253                         var pre = parentNode.lastChild;
66254                         cp.removeChild(newChild);//remove and update
66255                         var pre = parentNode.lastChild;
66256                 }
66257                 var pre = parentNode.lastChild;
66258                 newChild.parentNode = parentNode;
66259                 newChild.previousSibling = pre;
66260                 newChild.nextSibling = null;
66261                 if(pre){
66262                         pre.nextSibling = newChild;
66263                 }else {
66264                         parentNode.firstChild = newChild;
66265                 }
66266                 parentNode.lastChild = newChild;
66267                 _onUpdateChild(parentNode.ownerDocument,parentNode,newChild);
66268                 return newChild;
66269                 //console.log("__aa",parentNode.lastChild.nextSibling == null)
66270         }
66271         Document.prototype = {
66272                 //implementation : null,
66273                 nodeName :  '#document',
66274                 nodeType :  DOCUMENT_NODE,
66275                 doctype :  null,
66276                 documentElement :  null,
66277                 _inc : 1,
66278                 
66279                 insertBefore :  function(newChild, refChild){//raises 
66280                         if(newChild.nodeType == DOCUMENT_FRAGMENT_NODE){
66281                                 var child = newChild.firstChild;
66282                                 while(child){
66283                                         var next = child.nextSibling;
66284                                         this.insertBefore(child,refChild);
66285                                         child = next;
66286                                 }
66287                                 return newChild;
66288                         }
66289                         if(this.documentElement == null && newChild.nodeType == ELEMENT_NODE){
66290                                 this.documentElement = newChild;
66291                         }
66292                         
66293                         return _insertBefore(this,newChild,refChild),(newChild.ownerDocument = this),newChild;
66294                 },
66295                 removeChild :  function(oldChild){
66296                         if(this.documentElement == oldChild){
66297                                 this.documentElement = null;
66298                         }
66299                         return _removeChild(this,oldChild);
66300                 },
66301                 // Introduced in DOM Level 2:
66302                 importNode : function(importedNode,deep){
66303                         return importNode(this,importedNode,deep);
66304                 },
66305                 // Introduced in DOM Level 2:
66306                 getElementById :        function(id){
66307                         var rtv = null;
66308                         _visitNode(this.documentElement,function(node){
66309                                 if(node.nodeType == ELEMENT_NODE){
66310                                         if(node.getAttribute('id') == id){
66311                                                 rtv = node;
66312                                                 return true;
66313                                         }
66314                                 }
66315                         });
66316                         return rtv;
66317                 },
66318                 
66319                 //document factory method:
66320                 createElement : function(tagName){
66321                         var node = new Element();
66322                         node.ownerDocument = this;
66323                         node.nodeName = tagName;
66324                         node.tagName = tagName;
66325                         node.childNodes = new NodeList();
66326                         var attrs       = node.attributes = new NamedNodeMap();
66327                         attrs._ownerElement = node;
66328                         return node;
66329                 },
66330                 createDocumentFragment :        function(){
66331                         var node = new DocumentFragment();
66332                         node.ownerDocument = this;
66333                         node.childNodes = new NodeList();
66334                         return node;
66335                 },
66336                 createTextNode :        function(data){
66337                         var node = new Text();
66338                         node.ownerDocument = this;
66339                         node.appendData(data);
66340                         return node;
66341                 },
66342                 createComment : function(data){
66343                         var node = new Comment();
66344                         node.ownerDocument = this;
66345                         node.appendData(data);
66346                         return node;
66347                 },
66348                 createCDATASection :    function(data){
66349                         var node = new CDATASection();
66350                         node.ownerDocument = this;
66351                         node.appendData(data);
66352                         return node;
66353                 },
66354                 createProcessingInstruction :   function(target,data){
66355                         var node = new ProcessingInstruction();
66356                         node.ownerDocument = this;
66357                         node.tagName = node.target = target;
66358                         node.nodeValue= node.data = data;
66359                         return node;
66360                 },
66361                 createAttribute :       function(name){
66362                         var node = new Attr();
66363                         node.ownerDocument      = this;
66364                         node.name = name;
66365                         node.nodeName   = name;
66366                         node.localName = name;
66367                         node.specified = true;
66368                         return node;
66369                 },
66370                 createEntityReference : function(name){
66371                         var node = new EntityReference();
66372                         node.ownerDocument      = this;
66373                         node.nodeName   = name;
66374                         return node;
66375                 },
66376                 // Introduced in DOM Level 2:
66377                 createElementNS :       function(namespaceURI,qualifiedName){
66378                         var node = new Element();
66379                         var pl = qualifiedName.split(':');
66380                         var attrs       = node.attributes = new NamedNodeMap();
66381                         node.childNodes = new NodeList();
66382                         node.ownerDocument = this;
66383                         node.nodeName = qualifiedName;
66384                         node.tagName = qualifiedName;
66385                         node.namespaceURI = namespaceURI;
66386                         if(pl.length == 2){
66387                                 node.prefix = pl[0];
66388                                 node.localName = pl[1];
66389                         }else {
66390                                 //el.prefix = null;
66391                                 node.localName = qualifiedName;
66392                         }
66393                         attrs._ownerElement = node;
66394                         return node;
66395                 },
66396                 // Introduced in DOM Level 2:
66397                 createAttributeNS :     function(namespaceURI,qualifiedName){
66398                         var node = new Attr();
66399                         var pl = qualifiedName.split(':');
66400                         node.ownerDocument = this;
66401                         node.nodeName = qualifiedName;
66402                         node.name = qualifiedName;
66403                         node.namespaceURI = namespaceURI;
66404                         node.specified = true;
66405                         if(pl.length == 2){
66406                                 node.prefix = pl[0];
66407                                 node.localName = pl[1];
66408                         }else {
66409                                 //el.prefix = null;
66410                                 node.localName = qualifiedName;
66411                         }
66412                         return node;
66413                 }
66414         };
66415         _extends(Document,Node);
66416
66417
66418         function Element() {
66419                 this._nsMap = {};
66420         }Element.prototype = {
66421                 nodeType : ELEMENT_NODE,
66422                 hasAttribute : function(name){
66423                         return this.getAttributeNode(name)!=null;
66424                 },
66425                 getAttribute : function(name){
66426                         var attr = this.getAttributeNode(name);
66427                         return attr && attr.value || '';
66428                 },
66429                 getAttributeNode : function(name){
66430                         return this.attributes.getNamedItem(name);
66431                 },
66432                 setAttribute : function(name, value){
66433                         var attr = this.ownerDocument.createAttribute(name);
66434                         attr.value = attr.nodeValue = "" + value;
66435                         this.setAttributeNode(attr);
66436                 },
66437                 removeAttribute : function(name){
66438                         var attr = this.getAttributeNode(name);
66439                         attr && this.removeAttributeNode(attr);
66440                 },
66441                 
66442                 //four real opeartion method
66443                 appendChild:function(newChild){
66444                         if(newChild.nodeType === DOCUMENT_FRAGMENT_NODE){
66445                                 return this.insertBefore(newChild,null);
66446                         }else {
66447                                 return _appendSingleChild(this,newChild);
66448                         }
66449                 },
66450                 setAttributeNode : function(newAttr){
66451                         return this.attributes.setNamedItem(newAttr);
66452                 },
66453                 setAttributeNodeNS : function(newAttr){
66454                         return this.attributes.setNamedItemNS(newAttr);
66455                 },
66456                 removeAttributeNode : function(oldAttr){
66457                         //console.log(this == oldAttr.ownerElement)
66458                         return this.attributes.removeNamedItem(oldAttr.nodeName);
66459                 },
66460                 //get real attribute name,and remove it by removeAttributeNode
66461                 removeAttributeNS : function(namespaceURI, localName){
66462                         var old = this.getAttributeNodeNS(namespaceURI, localName);
66463                         old && this.removeAttributeNode(old);
66464                 },
66465                 
66466                 hasAttributeNS : function(namespaceURI, localName){
66467                         return this.getAttributeNodeNS(namespaceURI, localName)!=null;
66468                 },
66469                 getAttributeNS : function(namespaceURI, localName){
66470                         var attr = this.getAttributeNodeNS(namespaceURI, localName);
66471                         return attr && attr.value || '';
66472                 },
66473                 setAttributeNS : function(namespaceURI, qualifiedName, value){
66474                         var attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
66475                         attr.value = attr.nodeValue = "" + value;
66476                         this.setAttributeNode(attr);
66477                 },
66478                 getAttributeNodeNS : function(namespaceURI, localName){
66479                         return this.attributes.getNamedItemNS(namespaceURI, localName);
66480                 },
66481                 
66482                 getElementsByTagName : function(tagName){
66483                         return new LiveNodeList(this,function(base){
66484                                 var ls = [];
66485                                 _visitNode(base,function(node){
66486                                         if(node !== base && node.nodeType == ELEMENT_NODE && (tagName === '*' || node.tagName == tagName)){
66487                                                 ls.push(node);
66488                                         }
66489                                 });
66490                                 return ls;
66491                         });
66492                 },
66493                 getElementsByTagNameNS : function(namespaceURI, localName){
66494                         return new LiveNodeList(this,function(base){
66495                                 var ls = [];
66496                                 _visitNode(base,function(node){
66497                                         if(node !== base && node.nodeType === ELEMENT_NODE && (namespaceURI === '*' || node.namespaceURI === namespaceURI) && (localName === '*' || node.localName == localName)){
66498                                                 ls.push(node);
66499                                         }
66500                                 });
66501                                 return ls;
66502                                 
66503                         });
66504                 }
66505         };
66506         Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
66507         Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
66508
66509
66510         _extends(Element,Node);
66511         function Attr() {
66512         }Attr.prototype.nodeType = ATTRIBUTE_NODE;
66513         _extends(Attr,Node);
66514
66515
66516         function CharacterData() {
66517         }CharacterData.prototype = {
66518                 data : '',
66519                 substringData : function(offset, count) {
66520                         return this.data.substring(offset, offset+count);
66521                 },
66522                 appendData: function(text) {
66523                         text = this.data+text;
66524                         this.nodeValue = this.data = text;
66525                         this.length = text.length;
66526                 },
66527                 insertData: function(offset,text) {
66528                         this.replaceData(offset,0,text);
66529                 
66530                 },
66531                 appendChild:function(newChild){
66532                         throw new Error(ExceptionMessage[HIERARCHY_REQUEST_ERR])
66533                 },
66534                 deleteData: function(offset, count) {
66535                         this.replaceData(offset,count,"");
66536                 },
66537                 replaceData: function(offset, count, text) {
66538                         var start = this.data.substring(0,offset);
66539                         var end = this.data.substring(offset+count);
66540                         text = start + text + end;
66541                         this.nodeValue = this.data = text;
66542                         this.length = text.length;
66543                 }
66544         };
66545         _extends(CharacterData,Node);
66546         function Text() {
66547         }Text.prototype = {
66548                 nodeName : "#text",
66549                 nodeType : TEXT_NODE,
66550                 splitText : function(offset) {
66551                         var text = this.data;
66552                         var newText = text.substring(offset);
66553                         text = text.substring(0, offset);
66554                         this.data = this.nodeValue = text;
66555                         this.length = text.length;
66556                         var newNode = this.ownerDocument.createTextNode(newText);
66557                         if(this.parentNode){
66558                                 this.parentNode.insertBefore(newNode, this.nextSibling);
66559                         }
66560                         return newNode;
66561                 }
66562         };
66563         _extends(Text,CharacterData);
66564         function Comment() {
66565         }Comment.prototype = {
66566                 nodeName : "#comment",
66567                 nodeType : COMMENT_NODE
66568         };
66569         _extends(Comment,CharacterData);
66570
66571         function CDATASection() {
66572         }CDATASection.prototype = {
66573                 nodeName : "#cdata-section",
66574                 nodeType : CDATA_SECTION_NODE
66575         };
66576         _extends(CDATASection,CharacterData);
66577
66578
66579         function DocumentType() {
66580         }DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
66581         _extends(DocumentType,Node);
66582
66583         function Notation() {
66584         }Notation.prototype.nodeType = NOTATION_NODE;
66585         _extends(Notation,Node);
66586
66587         function Entity() {
66588         }Entity.prototype.nodeType = ENTITY_NODE;
66589         _extends(Entity,Node);
66590
66591         function EntityReference() {
66592         }EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
66593         _extends(EntityReference,Node);
66594
66595         function DocumentFragment() {
66596         }DocumentFragment.prototype.nodeName =  "#document-fragment";
66597         DocumentFragment.prototype.nodeType =   DOCUMENT_FRAGMENT_NODE;
66598         _extends(DocumentFragment,Node);
66599
66600
66601         function ProcessingInstruction() {
66602         }
66603         ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
66604         _extends(ProcessingInstruction,Node);
66605         function XMLSerializer$1(){}
66606         XMLSerializer$1.prototype.serializeToString = function(node,isHtml,nodeFilter){
66607                 return nodeSerializeToString.call(node,isHtml,nodeFilter);
66608         };
66609         Node.prototype.toString = nodeSerializeToString;
66610         function nodeSerializeToString(isHtml,nodeFilter){
66611                 var buf = [];
66612                 var refNode = this.nodeType == 9?this.documentElement:this;
66613                 var prefix = refNode.prefix;
66614                 var uri = refNode.namespaceURI;
66615                 
66616                 if(uri && prefix == null){
66617                         //console.log(prefix)
66618                         var prefix = refNode.lookupPrefix(uri);
66619                         if(prefix == null){
66620                                 //isHTML = true;
66621                                 var visibleNamespaces=[
66622                                 {namespace:uri,prefix:null} ];
66623                         }
66624                 }
66625                 serializeToString(this,buf,isHtml,nodeFilter,visibleNamespaces);
66626                 //console.log('###',this.nodeType,uri,prefix,buf.join(''))
66627                 return buf.join('');
66628         }
66629         function needNamespaceDefine(node,isHTML, visibleNamespaces) {
66630                 var prefix = node.prefix||'';
66631                 var uri = node.namespaceURI;
66632                 if (!prefix && !uri){
66633                         return false;
66634                 }
66635                 if (prefix === "xml" && uri === "http://www.w3.org/XML/1998/namespace" 
66636                         || uri == 'http://www.w3.org/2000/xmlns/'){
66637                         return false;
66638                 }
66639                 
66640                 var i = visibleNamespaces.length; 
66641                 //console.log('@@@@',node.tagName,prefix,uri,visibleNamespaces)
66642                 while (i--) {
66643                         var ns = visibleNamespaces[i];
66644                         // get namespace prefix
66645                         //console.log(node.nodeType,node.tagName,ns.prefix,prefix)
66646                         if (ns.prefix == prefix){
66647                                 return ns.namespace != uri;
66648                         }
66649                 }
66650                 //console.log(isHTML,uri,prefix=='')
66651                 //if(isHTML && prefix ==null && uri == 'http://www.w3.org/1999/xhtml'){
66652                 //      return false;
66653                 //}
66654                 //node.flag = '11111'
66655                 //console.error(3,true,node.flag,node.prefix,node.namespaceURI)
66656                 return true;
66657         }
66658         function serializeToString(node,buf,isHTML,nodeFilter,visibleNamespaces){
66659                 if(nodeFilter){
66660                         node = nodeFilter(node);
66661                         if(node){
66662                                 if(typeof node == 'string'){
66663                                         buf.push(node);
66664                                         return;
66665                                 }
66666                         }else {
66667                                 return;
66668                         }
66669                         //buf.sort.apply(attrs, attributeSorter);
66670                 }
66671                 switch(node.nodeType){
66672                 case ELEMENT_NODE:
66673                         if (!visibleNamespaces) { visibleNamespaces = []; }
66674                         var startVisibleNamespaces = visibleNamespaces.length;
66675                         var attrs = node.attributes;
66676                         var len = attrs.length;
66677                         var child = node.firstChild;
66678                         var nodeName = node.tagName;
66679                         
66680                         isHTML =  (htmlns === node.namespaceURI) ||isHTML; 
66681                         buf.push('<',nodeName);
66682                         
66683                         
66684                         
66685                         for(var i=0;i<len;i++){
66686                                 // add namespaces for attributes
66687                                 var attr = attrs.item(i);
66688                                 if (attr.prefix == 'xmlns') {
66689                                         visibleNamespaces.push({ prefix: attr.localName, namespace: attr.value });
66690                                 }else if(attr.nodeName == 'xmlns'){
66691                                         visibleNamespaces.push({ prefix: '', namespace: attr.value });
66692                                 }
66693                         }
66694                         for(var i=0;i<len;i++){
66695                                 var attr = attrs.item(i);
66696                                 if (needNamespaceDefine(attr,isHTML, visibleNamespaces)) {
66697                                         var prefix = attr.prefix||'';
66698                                         var uri = attr.namespaceURI;
66699                                         var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66700                                         buf.push(ns, '="' , uri , '"');
66701                                         visibleNamespaces.push({ prefix: prefix, namespace:uri });
66702                                 }
66703                                 serializeToString(attr,buf,isHTML,nodeFilter,visibleNamespaces);
66704                         }
66705                         // add namespace for current node               
66706                         if (needNamespaceDefine(node,isHTML, visibleNamespaces)) {
66707                                 var prefix = node.prefix||'';
66708                                 var uri = node.namespaceURI;
66709                                 var ns = prefix ? ' xmlns:' + prefix : " xmlns";
66710                                 buf.push(ns, '="' , uri , '"');
66711                                 visibleNamespaces.push({ prefix: prefix, namespace:uri });
66712                         }
66713                         
66714                         if(child || isHTML && !/^(?:meta|link|img|br|hr|input)$/i.test(nodeName)){
66715                                 buf.push('>');
66716                                 //if is cdata child node
66717                                 if(isHTML && /^script$/i.test(nodeName)){
66718                                         while(child){
66719                                                 if(child.data){
66720                                                         buf.push(child.data);
66721                                                 }else {
66722                                                         serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66723                                                 }
66724                                                 child = child.nextSibling;
66725                                         }
66726                                 }else
66727                                 {
66728                                         while(child){
66729                                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66730                                                 child = child.nextSibling;
66731                                         }
66732                                 }
66733                                 buf.push('</',nodeName,'>');
66734                         }else {
66735                                 buf.push('/>');
66736                         }
66737                         // remove added visible namespaces
66738                         //visibleNamespaces.length = startVisibleNamespaces;
66739                         return;
66740                 case DOCUMENT_NODE:
66741                 case DOCUMENT_FRAGMENT_NODE:
66742                         var child = node.firstChild;
66743                         while(child){
66744                                 serializeToString(child,buf,isHTML,nodeFilter,visibleNamespaces);
66745                                 child = child.nextSibling;
66746                         }
66747                         return;
66748                 case ATTRIBUTE_NODE:
66749                         return buf.push(' ',node.name,'="',node.value.replace(/[<&"]/g,_xmlEncoder),'"');
66750                 case TEXT_NODE:
66751                         return buf.push(node.data.replace(/[<&]/g,_xmlEncoder));
66752                 case CDATA_SECTION_NODE:
66753                         return buf.push( '<![CDATA[',node.data,']]>');
66754                 case COMMENT_NODE:
66755                         return buf.push( "<!--",node.data,"-->");
66756                 case DOCUMENT_TYPE_NODE:
66757                         var pubid = node.publicId;
66758                         var sysid = node.systemId;
66759                         buf.push('<!DOCTYPE ',node.name);
66760                         if(pubid){
66761                                 buf.push(' PUBLIC "',pubid);
66762                                 if (sysid && sysid!='.') {
66763                                         buf.push( '" "',sysid);
66764                                 }
66765                                 buf.push('">');
66766                         }else if(sysid && sysid!='.'){
66767                                 buf.push(' SYSTEM "',sysid,'">');
66768                         }else {
66769                                 var sub = node.internalSubset;
66770                                 if(sub){
66771                                         buf.push(" [",sub,"]");
66772                                 }
66773                                 buf.push(">");
66774                         }
66775                         return;
66776                 case PROCESSING_INSTRUCTION_NODE:
66777                         return buf.push( "<?",node.target," ",node.data,"?>");
66778                 case ENTITY_REFERENCE_NODE:
66779                         return buf.push( '&',node.nodeName,';');
66780                 //case ENTITY_NODE:
66781                 //case NOTATION_NODE:
66782                 default:
66783                         buf.push('??',node.nodeName);
66784                 }
66785         }
66786         function importNode(doc,node,deep){
66787                 var node2;
66788                 switch (node.nodeType) {
66789                 case ELEMENT_NODE:
66790                         node2 = node.cloneNode(false);
66791                         node2.ownerDocument = doc;
66792                         //var attrs = node2.attributes;
66793                         //var len = attrs.length;
66794                         //for(var i=0;i<len;i++){
66795                                 //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
66796                         //}
66797                 case DOCUMENT_FRAGMENT_NODE:
66798                         break;
66799                 case ATTRIBUTE_NODE:
66800                         deep = true;
66801                         break;
66802                 //case ENTITY_REFERENCE_NODE:
66803                 //case PROCESSING_INSTRUCTION_NODE:
66804                 ////case TEXT_NODE:
66805                 //case CDATA_SECTION_NODE:
66806                 //case COMMENT_NODE:
66807                 //      deep = false;
66808                 //      break;
66809                 //case DOCUMENT_NODE:
66810                 //case DOCUMENT_TYPE_NODE:
66811                 //cannot be imported.
66812                 //case ENTITY_NODE:
66813                 //case NOTATION_NODE:
66814                 //can not hit in level3
66815                 //default:throw e;
66816                 }
66817                 if(!node2){
66818                         node2 = node.cloneNode(false);//false
66819                 }
66820                 node2.ownerDocument = doc;
66821                 node2.parentNode = null;
66822                 if(deep){
66823                         var child = node.firstChild;
66824                         while(child){
66825                                 node2.appendChild(importNode(doc,child,deep));
66826                                 child = child.nextSibling;
66827                         }
66828                 }
66829                 return node2;
66830         }
66831         //
66832         //var _relationMap = {firstChild:1,lastChild:1,previousSibling:1,nextSibling:1,
66833         //                                      attributes:1,childNodes:1,parentNode:1,documentElement:1,doctype,};
66834         function cloneNode(doc,node,deep){
66835                 var node2 = new node.constructor();
66836                 for(var n in node){
66837                         var v = node[n];
66838                         if(typeof v != 'object' ){
66839                                 if(v != node2[n]){
66840                                         node2[n] = v;
66841                                 }
66842                         }
66843                 }
66844                 if(node.childNodes){
66845                         node2.childNodes = new NodeList();
66846                 }
66847                 node2.ownerDocument = doc;
66848                 switch (node2.nodeType) {
66849                 case ELEMENT_NODE:
66850                         var attrs       = node.attributes;
66851                         var attrs2      = node2.attributes = new NamedNodeMap();
66852                         var len = attrs.length;
66853                         attrs2._ownerElement = node2;
66854                         for(var i=0;i<len;i++){
66855                                 node2.setAttributeNode(cloneNode(doc,attrs.item(i),true));
66856                         }
66857                         break;  case ATTRIBUTE_NODE:
66858                         deep = true;
66859                 }
66860                 if(deep){
66861                         var child = node.firstChild;
66862                         while(child){
66863                                 node2.appendChild(cloneNode(doc,child,deep));
66864                                 child = child.nextSibling;
66865                         }
66866                 }
66867                 return node2;
66868         }
66869
66870         function __set__(object,key,value){
66871                 object[key] = value;
66872         }
66873         //do dynamic
66874         try{
66875                 if(Object.defineProperty){
66876                         Object.defineProperty(LiveNodeList.prototype,'length',{
66877                                 get:function(){
66878                                         _updateLiveList(this);
66879                                         return this.$$length;
66880                                 }
66881                         });
66882                         Object.defineProperty(Node.prototype,'textContent',{
66883                                 get:function(){
66884                                         return getTextContent(this);
66885                                 },
66886                                 set:function(data){
66887                                         switch(this.nodeType){
66888                                         case ELEMENT_NODE:
66889                                         case DOCUMENT_FRAGMENT_NODE:
66890                                                 while(this.firstChild){
66891                                                         this.removeChild(this.firstChild);
66892                                                 }
66893                                                 if(data || String(data)){
66894                                                         this.appendChild(this.ownerDocument.createTextNode(data));
66895                                                 }
66896                                                 break;
66897                                         default:
66898                                                 //TODO:
66899                                                 this.data = data;
66900                                                 this.value = data;
66901                                                 this.nodeValue = data;
66902                                         }
66903                                 }
66904                         });
66905                         
66906                         function getTextContent(node){
66907                                 switch(node.nodeType){
66908                                 case ELEMENT_NODE:
66909                                 case DOCUMENT_FRAGMENT_NODE:
66910                                         var buf = [];
66911                                         node = node.firstChild;
66912                                         while(node){
66913                                                 if(node.nodeType!==7 && node.nodeType !==8){
66914                                                         buf.push(getTextContent(node));
66915                                                 }
66916                                                 node = node.nextSibling;
66917                                         }
66918                                         return buf.join('');
66919                                 default:
66920                                         return node.nodeValue;
66921                                 }
66922                         }
66923                         __set__ = function(object,key,value){
66924                                 //console.log(value)
66925                                 object['$$'+key] = value;
66926                         };
66927                 }
66928         }catch(e){//ie8
66929         }
66930
66931         //if(typeof require == 'function'){
66932                 var DOMImplementation_1 = DOMImplementation;
66933                 var XMLSerializer_1 = XMLSerializer$1;
66934         //}
66935
66936         var dom = {
66937                 DOMImplementation: DOMImplementation_1,
66938                 XMLSerializer: XMLSerializer_1
66939         };
66940
66941         var domParser = createCommonjsModule(function (module, exports) {
66942         function DOMParser(options){
66943                 this.options = options ||{locator:{}};
66944                 
66945         }
66946         DOMParser.prototype.parseFromString = function(source,mimeType){
66947                 var options = this.options;
66948                 var sax =  new XMLReader();
66949                 var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler
66950                 var errorHandler = options.errorHandler;
66951                 var locator = options.locator;
66952                 var defaultNSMap = options.xmlns||{};
66953                 var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"};
66954                 if(locator){
66955                         domBuilder.setDocumentLocator(locator);
66956                 }
66957                 
66958                 sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator);
66959                 sax.domBuilder = options.domBuilder || domBuilder;
66960                 if(/\/x?html?$/.test(mimeType)){
66961                         entityMap.nbsp = '\xa0';
66962                         entityMap.copy = '\xa9';
66963                         defaultNSMap['']= 'http://www.w3.org/1999/xhtml';
66964                 }
66965                 defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace';
66966                 if(source){
66967                         sax.parse(source,defaultNSMap,entityMap);
66968                 }else {
66969                         sax.errorHandler.error("invalid doc source");
66970                 }
66971                 return domBuilder.doc;
66972         };
66973         function buildErrorHandler(errorImpl,domBuilder,locator){
66974                 if(!errorImpl){
66975                         if(domBuilder instanceof DOMHandler){
66976                                 return domBuilder;
66977                         }
66978                         errorImpl = domBuilder ;
66979                 }
66980                 var errorHandler = {};
66981                 var isCallback = errorImpl instanceof Function;
66982                 locator = locator||{};
66983                 function build(key){
66984                         var fn = errorImpl[key];
66985                         if(!fn && isCallback){
66986                                 fn = errorImpl.length == 2?function(msg){errorImpl(key,msg);}:errorImpl;
66987                         }
66988                         errorHandler[key] = fn && function(msg){
66989                                 fn('[xmldom '+key+']\t'+msg+_locator(locator));
66990                         }||function(){};
66991                 }
66992                 build('warning');
66993                 build('error');
66994                 build('fatalError');
66995                 return errorHandler;
66996         }
66997
66998         //console.log('#\n\n\n\n\n\n\n####')
66999         /**
67000          * +ContentHandler+ErrorHandler
67001          * +LexicalHandler+EntityResolver2
67002          * -DeclHandler-DTDHandler 
67003          * 
67004          * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler
67005          * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2
67006          * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html
67007          */
67008         function DOMHandler() {
67009             this.cdata = false;
67010         }
67011         function position(locator,node){
67012                 node.lineNumber = locator.lineNumber;
67013                 node.columnNumber = locator.columnNumber;
67014         }
67015         /**
67016          * @see org.xml.sax.ContentHandler#startDocument
67017          * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html
67018          */ 
67019         DOMHandler.prototype = {
67020                 startDocument : function() {
67021                 this.doc = new DOMImplementation().createDocument(null, null, null);
67022                 if (this.locator) {
67023                         this.doc.documentURI = this.locator.systemId;
67024                 }
67025                 },
67026                 startElement:function(namespaceURI, localName, qName, attrs) {
67027                         var doc = this.doc;
67028                     var el = doc.createElementNS(namespaceURI, qName||localName);
67029                     var len = attrs.length;
67030                     appendElement(this, el);
67031                     this.currentElement = el;
67032                     
67033                         this.locator && position(this.locator,el);
67034                     for (var i = 0 ; i < len; i++) {
67035                         var namespaceURI = attrs.getURI(i);
67036                         var value = attrs.getValue(i);
67037                         var qName = attrs.getQName(i);
67038                                 var attr = doc.createAttributeNS(namespaceURI, qName);
67039                                 this.locator &&position(attrs.getLocator(i),attr);
67040                                 attr.value = attr.nodeValue = value;
67041                                 el.setAttributeNode(attr);
67042                     }
67043                 },
67044                 endElement:function(namespaceURI, localName, qName) {
67045                         var current = this.currentElement;
67046                         var tagName = current.tagName;
67047                         this.currentElement = current.parentNode;
67048                 },
67049                 startPrefixMapping:function(prefix, uri) {
67050                 },
67051                 endPrefixMapping:function(prefix) {
67052                 },
67053                 processingInstruction:function(target, data) {
67054                     var ins = this.doc.createProcessingInstruction(target, data);
67055                     this.locator && position(this.locator,ins);
67056                     appendElement(this, ins);
67057                 },
67058                 ignorableWhitespace:function(ch, start, length) {
67059                 },
67060                 characters:function(chars, start, length) {
67061                         chars = _toString.apply(this,arguments);
67062                         //console.log(chars)
67063                         if(chars){
67064                                 if (this.cdata) {
67065                                         var charNode = this.doc.createCDATASection(chars);
67066                                 } else {
67067                                         var charNode = this.doc.createTextNode(chars);
67068                                 }
67069                                 if(this.currentElement){
67070                                         this.currentElement.appendChild(charNode);
67071                                 }else if(/^\s*$/.test(chars)){
67072                                         this.doc.appendChild(charNode);
67073                                         //process xml
67074                                 }
67075                                 this.locator && position(this.locator,charNode);
67076                         }
67077                 },
67078                 skippedEntity:function(name) {
67079                 },
67080                 endDocument:function() {
67081                         this.doc.normalize();
67082                 },
67083                 setDocumentLocator:function (locator) {
67084                     if(this.locator = locator){// && !('lineNumber' in locator)){
67085                         locator.lineNumber = 0;
67086                     }
67087                 },
67088                 //LexicalHandler
67089                 comment:function(chars, start, length) {
67090                         chars = _toString.apply(this,arguments);
67091                     var comm = this.doc.createComment(chars);
67092                     this.locator && position(this.locator,comm);
67093                     appendElement(this, comm);
67094                 },
67095                 
67096                 startCDATA:function() {
67097                     //used in characters() methods
67098                     this.cdata = true;
67099                 },
67100                 endCDATA:function() {
67101                     this.cdata = false;
67102                 },
67103                 
67104                 startDTD:function(name, publicId, systemId) {
67105                         var impl = this.doc.implementation;
67106                     if (impl && impl.createDocumentType) {
67107                         var dt = impl.createDocumentType(name, publicId, systemId);
67108                         this.locator && position(this.locator,dt);
67109                         appendElement(this, dt);
67110                     }
67111                 },
67112                 /**
67113                  * @see org.xml.sax.ErrorHandler
67114                  * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html
67115                  */
67116                 warning:function(error) {
67117                         console.warn('[xmldom warning]\t'+error,_locator(this.locator));
67118                 },
67119                 error:function(error) {
67120                         console.error('[xmldom error]\t'+error,_locator(this.locator));
67121                 },
67122                 fatalError:function(error) {
67123                         console.error('[xmldom fatalError]\t'+error,_locator(this.locator));
67124                     throw error;
67125                 }
67126         };
67127         function _locator(l){
67128                 if(l){
67129                         return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']'
67130                 }
67131         }
67132         function _toString(chars,start,length){
67133                 if(typeof chars == 'string'){
67134                         return chars.substr(start,length)
67135                 }else {//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)")
67136                         if(chars.length >= start+length || start){
67137                                 return new java.lang.String(chars,start,length)+'';
67138                         }
67139                         return chars;
67140                 }
67141         }
67142
67143         /*
67144          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html
67145          * used method of org.xml.sax.ext.LexicalHandler:
67146          *  #comment(chars, start, length)
67147          *  #startCDATA()
67148          *  #endCDATA()
67149          *  #startDTD(name, publicId, systemId)
67150          *
67151          *
67152          * IGNORED method of org.xml.sax.ext.LexicalHandler:
67153          *  #endDTD()
67154          *  #startEntity(name)
67155          *  #endEntity(name)
67156          *
67157          *
67158          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html
67159          * IGNORED method of org.xml.sax.ext.DeclHandler
67160          *      #attributeDecl(eName, aName, type, mode, value)
67161          *  #elementDecl(name, model)
67162          *  #externalEntityDecl(name, publicId, systemId)
67163          *  #internalEntityDecl(name, value)
67164          * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html
67165          * IGNORED method of org.xml.sax.EntityResolver2
67166          *  #resolveEntity(String name,String publicId,String baseURI,String systemId)
67167          *  #resolveEntity(publicId, systemId)
67168          *  #getExternalSubset(name, baseURI)
67169          * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html
67170          * IGNORED method of org.xml.sax.DTDHandler
67171          *  #notationDecl(name, publicId, systemId) {};
67172          *  #unparsedEntityDecl(name, publicId, systemId, notationName) {};
67173          */
67174         "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){
67175                 DOMHandler.prototype[key] = function(){return null};
67176         });
67177
67178         /* 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 */
67179         function appendElement (hander,node) {
67180             if (!hander.currentElement) {
67181                 hander.doc.appendChild(node);
67182             } else {
67183                 hander.currentElement.appendChild(node);
67184             }
67185         }//appendChild and setAttributeNS are preformance key
67186
67187         //if(typeof require == 'function'){
67188                 var XMLReader = sax.XMLReader;
67189                 var DOMImplementation = exports.DOMImplementation = dom.DOMImplementation;
67190                 exports.XMLSerializer = dom.XMLSerializer ;
67191                 exports.DOMParser = DOMParser;
67192         //}
67193         });
67194
67195         var togeojson = createCommonjsModule(function (module, exports) {
67196         var toGeoJSON = (function() {
67197
67198             var removeSpace = /\s*/g,
67199                 trimSpace = /^\s*|\s*$/g,
67200                 splitSpace = /\s+/;
67201             // generate a short, numeric hash of a string
67202             function okhash(x) {
67203                 if (!x || !x.length) { return 0; }
67204                 for (var i = 0, h = 0; i < x.length; i++) {
67205                     h = ((h << 5) - h) + x.charCodeAt(i) | 0;
67206                 } return h;
67207             }
67208             // all Y children of X
67209             function get(x, y) { return x.getElementsByTagName(y); }
67210             function attr(x, y) { return x.getAttribute(y); }
67211             function attrf(x, y) { return parseFloat(attr(x, y)); }
67212             // one Y child of X, if any, otherwise null
67213             function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
67214             // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
67215             function norm(el) { if (el.normalize) { el.normalize(); } return el; }
67216             // cast array x into numbers
67217             function numarray(x) {
67218                 for (var j = 0, o = []; j < x.length; j++) { o[j] = parseFloat(x[j]); }
67219                 return o;
67220             }
67221             // get the content of a text node, if any
67222             function nodeVal(x) {
67223                 if (x) { norm(x); }
67224                 return (x && x.textContent) || '';
67225             }
67226             // get the contents of multiple text nodes, if present
67227             function getMulti(x, ys) {
67228                 var o = {}, n, k;
67229                 for (k = 0; k < ys.length; k++) {
67230                     n = get1(x, ys[k]);
67231                     if (n) { o[ys[k]] = nodeVal(n); }
67232                 }
67233                 return o;
67234             }
67235             // add properties of Y to X, overwriting if present in both
67236             function extend(x, y) { for (var k in y) { x[k] = y[k]; } }
67237             // get one coordinate from a coordinate array, if any
67238             function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
67239             // get all coordinates from a coordinate array as [[],[]]
67240             function coord(v) {
67241                 var coords = v.replace(trimSpace, '').split(splitSpace),
67242                     o = [];
67243                 for (var i = 0; i < coords.length; i++) {
67244                     o.push(coord1(coords[i]));
67245                 }
67246                 return o;
67247             }
67248             function coordPair(x) {
67249                 var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
67250                     ele = get1(x, 'ele'),
67251                     // handle namespaced attribute in browser
67252                     heartRate = get1(x, 'gpxtpx:hr') || get1(x, 'hr'),
67253                     time = get1(x, 'time'),
67254                     e;
67255                 if (ele) {
67256                     e = parseFloat(nodeVal(ele));
67257                     if (!isNaN(e)) {
67258                         ll.push(e);
67259                     }
67260                 }
67261                 return {
67262                     coordinates: ll,
67263                     time: time ? nodeVal(time) : null,
67264                     heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null
67265                 };
67266             }
67267
67268             // create a new feature collection parent object
67269             function fc() {
67270                 return {
67271                     type: 'FeatureCollection',
67272                     features: []
67273                 };
67274             }
67275
67276             var serializer;
67277             if (typeof XMLSerializer !== 'undefined') {
67278                 /* istanbul ignore next */
67279                 serializer = new XMLSerializer();
67280             // only require xmldom in a node environment
67281             } else if ( typeof process === 'object' && !process.browser) {
67282                 serializer = new (domParser.XMLSerializer)();
67283             }
67284             function xml2str(str) {
67285                 // IE9 will create a new XMLSerializer but it'll crash immediately.
67286                 // This line is ignored because we don't run coverage tests in IE9
67287                 /* istanbul ignore next */
67288                 if (str.xml !== undefined) { return str.xml; }
67289                 return serializer.serializeToString(str);
67290             }
67291
67292             var t = {
67293                 kml: function(doc) {
67294
67295                     var gj = fc(),
67296                         // styleindex keeps track of hashed styles in order to match features
67297                         styleIndex = {}, styleByHash = {},
67298                         // stylemapindex keeps track of style maps to expose in properties
67299                         styleMapIndex = {},
67300                         // atomic geospatial types supported by KML - MultiGeometry is
67301                         // handled separately
67302                         geotypes = ['Polygon', 'LineString', 'Point', 'Track', 'gx:Track'],
67303                         // all root placemarks in the file
67304                         placemarks = get(doc, 'Placemark'),
67305                         styles = get(doc, 'Style'),
67306                         styleMaps = get(doc, 'StyleMap');
67307
67308                     for (var k = 0; k < styles.length; k++) {
67309                         var hash = okhash(xml2str(styles[k])).toString(16);
67310                         styleIndex['#' + attr(styles[k], 'id')] = hash;
67311                         styleByHash[hash] = styles[k];
67312                     }
67313                     for (var l = 0; l < styleMaps.length; l++) {
67314                         styleIndex['#' + attr(styleMaps[l], 'id')] = okhash(xml2str(styleMaps[l])).toString(16);
67315                         var pairs = get(styleMaps[l], 'Pair');
67316                         var pairsMap = {};
67317                         for (var m = 0; m < pairs.length; m++) {
67318                             pairsMap[nodeVal(get1(pairs[m], 'key'))] = nodeVal(get1(pairs[m], 'styleUrl'));
67319                         }
67320                         styleMapIndex['#' + attr(styleMaps[l], 'id')] = pairsMap;
67321
67322                     }
67323                     for (var j = 0; j < placemarks.length; j++) {
67324                         gj.features = gj.features.concat(getPlacemark(placemarks[j]));
67325                     }
67326                     function kmlColor(v) {
67327                         var color, opacity;
67328                         v = v || '';
67329                         if (v.substr(0, 1) === '#') { v = v.substr(1); }
67330                         if (v.length === 6 || v.length === 3) { color = v; }
67331                         if (v.length === 8) {
67332                             opacity = parseInt(v.substr(0, 2), 16) / 255;
67333                             color = '#' + v.substr(6, 2) +
67334                                 v.substr(4, 2) +
67335                                 v.substr(2, 2);
67336                         }
67337                         return [color, isNaN(opacity) ? undefined : opacity];
67338                     }
67339                     function gxCoord(v) { return numarray(v.split(' ')); }
67340                     function gxCoords(root) {
67341                         var elems = get(root, 'coord'), coords = [], times = [];
67342                         if (elems.length === 0) { elems = get(root, 'gx:coord'); }
67343                         for (var i = 0; i < elems.length; i++) { coords.push(gxCoord(nodeVal(elems[i]))); }
67344                         var timeElems = get(root, 'when');
67345                         for (var j = 0; j < timeElems.length; j++) { times.push(nodeVal(timeElems[j])); }
67346                         return {
67347                             coords: coords,
67348                             times: times
67349                         };
67350                     }
67351                     function getGeometry(root) {
67352                         var geomNode, geomNodes, i, j, k, geoms = [], coordTimes = [];
67353                         if (get1(root, 'MultiGeometry')) { return getGeometry(get1(root, 'MultiGeometry')); }
67354                         if (get1(root, 'MultiTrack')) { return getGeometry(get1(root, 'MultiTrack')); }
67355                         if (get1(root, 'gx:MultiTrack')) { return getGeometry(get1(root, 'gx:MultiTrack')); }
67356                         for (i = 0; i < geotypes.length; i++) {
67357                             geomNodes = get(root, geotypes[i]);
67358                             if (geomNodes) {
67359                                 for (j = 0; j < geomNodes.length; j++) {
67360                                     geomNode = geomNodes[j];
67361                                     if (geotypes[i] === 'Point') {
67362                                         geoms.push({
67363                                             type: 'Point',
67364                                             coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
67365                                         });
67366                                     } else if (geotypes[i] === 'LineString') {
67367                                         geoms.push({
67368                                             type: 'LineString',
67369                                             coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
67370                                         });
67371                                     } else if (geotypes[i] === 'Polygon') {
67372                                         var rings = get(geomNode, 'LinearRing'),
67373                                             coords = [];
67374                                         for (k = 0; k < rings.length; k++) {
67375                                             coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
67376                                         }
67377                                         geoms.push({
67378                                             type: 'Polygon',
67379                                             coordinates: coords
67380                                         });
67381                                     } else if (geotypes[i] === 'Track' ||
67382                                         geotypes[i] === 'gx:Track') {
67383                                         var track = gxCoords(geomNode);
67384                                         geoms.push({
67385                                             type: 'LineString',
67386                                             coordinates: track.coords
67387                                         });
67388                                         if (track.times.length) { coordTimes.push(track.times); }
67389                                     }
67390                                 }
67391                             }
67392                         }
67393                         return {
67394                             geoms: geoms,
67395                             coordTimes: coordTimes
67396                         };
67397                     }
67398                     function getPlacemark(root) {
67399                         var geomsAndTimes = getGeometry(root), i, properties = {},
67400                             name = nodeVal(get1(root, 'name')),
67401                             address = nodeVal(get1(root, 'address')),
67402                             styleUrl = nodeVal(get1(root, 'styleUrl')),
67403                             description = nodeVal(get1(root, 'description')),
67404                             timeSpan = get1(root, 'TimeSpan'),
67405                             timeStamp = get1(root, 'TimeStamp'),
67406                             extendedData = get1(root, 'ExtendedData'),
67407                             lineStyle = get1(root, 'LineStyle'),
67408                             polyStyle = get1(root, 'PolyStyle'),
67409                             visibility = get1(root, 'visibility');
67410
67411                         if (!geomsAndTimes.geoms.length) { return []; }
67412                         if (name) { properties.name = name; }
67413                         if (address) { properties.address = address; }
67414                         if (styleUrl) {
67415                             if (styleUrl[0] !== '#') {
67416                                 styleUrl = '#' + styleUrl;
67417                             }
67418
67419                             properties.styleUrl = styleUrl;
67420                             if (styleIndex[styleUrl]) {
67421                                 properties.styleHash = styleIndex[styleUrl];
67422                             }
67423                             if (styleMapIndex[styleUrl]) {
67424                                 properties.styleMapHash = styleMapIndex[styleUrl];
67425                                 properties.styleHash = styleIndex[styleMapIndex[styleUrl].normal];
67426                             }
67427                             // Try to populate the lineStyle or polyStyle since we got the style hash
67428                             var style = styleByHash[properties.styleHash];
67429                             if (style) {
67430                                 if (!lineStyle) { lineStyle = get1(style, 'LineStyle'); }
67431                                 if (!polyStyle) { polyStyle = get1(style, 'PolyStyle'); }
67432                             }
67433                         }
67434                         if (description) { properties.description = description; }
67435                         if (timeSpan) {
67436                             var begin = nodeVal(get1(timeSpan, 'begin'));
67437                             var end = nodeVal(get1(timeSpan, 'end'));
67438                             properties.timespan = { begin: begin, end: end };
67439                         }
67440                         if (timeStamp) {
67441                             properties.timestamp = nodeVal(get1(timeStamp, 'when'));
67442                         }
67443                         if (lineStyle) {
67444                             var linestyles = kmlColor(nodeVal(get1(lineStyle, 'color'))),
67445                                 color = linestyles[0],
67446                                 opacity = linestyles[1],
67447                                 width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67448                             if (color) { properties.stroke = color; }
67449                             if (!isNaN(opacity)) { properties['stroke-opacity'] = opacity; }
67450                             if (!isNaN(width)) { properties['stroke-width'] = width; }
67451                         }
67452                         if (polyStyle) {
67453                             var polystyles = kmlColor(nodeVal(get1(polyStyle, 'color'))),
67454                                 pcolor = polystyles[0],
67455                                 popacity = polystyles[1],
67456                                 fill = nodeVal(get1(polyStyle, 'fill')),
67457                                 outline = nodeVal(get1(polyStyle, 'outline'));
67458                             if (pcolor) { properties.fill = pcolor; }
67459                             if (!isNaN(popacity)) { properties['fill-opacity'] = popacity; }
67460                             if (fill) { properties['fill-opacity'] = fill === '1' ? properties['fill-opacity'] || 1 : 0; }
67461                             if (outline) { properties['stroke-opacity'] = outline === '1' ? properties['stroke-opacity'] || 1 : 0; }
67462                         }
67463                         if (extendedData) {
67464                             var datas = get(extendedData, 'Data'),
67465                                 simpleDatas = get(extendedData, 'SimpleData');
67466
67467                             for (i = 0; i < datas.length; i++) {
67468                                 properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
67469                             }
67470                             for (i = 0; i < simpleDatas.length; i++) {
67471                                 properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
67472                             }
67473                         }
67474                         if (visibility) {
67475                             properties.visibility = nodeVal(visibility);
67476                         }
67477                         if (geomsAndTimes.coordTimes.length) {
67478                             properties.coordTimes = (geomsAndTimes.coordTimes.length === 1) ?
67479                                 geomsAndTimes.coordTimes[0] : geomsAndTimes.coordTimes;
67480                         }
67481                         var feature = {
67482                             type: 'Feature',
67483                             geometry: (geomsAndTimes.geoms.length === 1) ? geomsAndTimes.geoms[0] : {
67484                                 type: 'GeometryCollection',
67485                                 geometries: geomsAndTimes.geoms
67486                             },
67487                             properties: properties
67488                         };
67489                         if (attr(root, 'id')) { feature.id = attr(root, 'id'); }
67490                         return [feature];
67491                     }
67492                     return gj;
67493                 },
67494                 gpx: function(doc) {
67495                     var i,
67496                         tracks = get(doc, 'trk'),
67497                         routes = get(doc, 'rte'),
67498                         waypoints = get(doc, 'wpt'),
67499                         // a feature collection
67500                         gj = fc(),
67501                         feature;
67502                     for (i = 0; i < tracks.length; i++) {
67503                         feature = getTrack(tracks[i]);
67504                         if (feature) { gj.features.push(feature); }
67505                     }
67506                     for (i = 0; i < routes.length; i++) {
67507                         feature = getRoute(routes[i]);
67508                         if (feature) { gj.features.push(feature); }
67509                     }
67510                     for (i = 0; i < waypoints.length; i++) {
67511                         gj.features.push(getPoint(waypoints[i]));
67512                     }
67513                     function getPoints(node, pointname) {
67514                         var pts = get(node, pointname),
67515                             line = [],
67516                             times = [],
67517                             heartRates = [],
67518                             l = pts.length;
67519                         if (l < 2) { return {}; }  // Invalid line in GeoJSON
67520                         for (var i = 0; i < l; i++) {
67521                             var c = coordPair(pts[i]);
67522                             line.push(c.coordinates);
67523                             if (c.time) { times.push(c.time); }
67524                             if (c.heartRate) { heartRates.push(c.heartRate); }
67525                         }
67526                         return {
67527                             line: line,
67528                             times: times,
67529                             heartRates: heartRates
67530                         };
67531                     }
67532                     function getTrack(node) {
67533                         var segments = get(node, 'trkseg'),
67534                             track = [],
67535                             times = [],
67536                             heartRates = [],
67537                             line;
67538                         for (var i = 0; i < segments.length; i++) {
67539                             line = getPoints(segments[i], 'trkpt');
67540                             if (line) {
67541                                 if (line.line) { track.push(line.line); }
67542                                 if (line.times && line.times.length) { times.push(line.times); }
67543                                 if (line.heartRates && line.heartRates.length) { heartRates.push(line.heartRates); }
67544                             }
67545                         }
67546                         if (track.length === 0) { return; }
67547                         var properties = getProperties(node);
67548                         extend(properties, getLineStyle(get1(node, 'extensions')));
67549                         if (times.length) { properties.coordTimes = track.length === 1 ? times[0] : times; }
67550                         if (heartRates.length) { properties.heartRates = track.length === 1 ? heartRates[0] : heartRates; }
67551                         return {
67552                             type: 'Feature',
67553                             properties: properties,
67554                             geometry: {
67555                                 type: track.length === 1 ? 'LineString' : 'MultiLineString',
67556                                 coordinates: track.length === 1 ? track[0] : track
67557                             }
67558                         };
67559                     }
67560                     function getRoute(node) {
67561                         var line = getPoints(node, 'rtept');
67562                         if (!line.line) { return; }
67563                         var prop = getProperties(node);
67564                         extend(prop, getLineStyle(get1(node, 'extensions')));
67565                         var routeObj = {
67566                             type: 'Feature',
67567                             properties: prop,
67568                             geometry: {
67569                                 type: 'LineString',
67570                                 coordinates: line.line
67571                             }
67572                         };
67573                         return routeObj;
67574                     }
67575                     function getPoint(node) {
67576                         var prop = getProperties(node);
67577                         extend(prop, getMulti(node, ['sym']));
67578                         return {
67579                             type: 'Feature',
67580                             properties: prop,
67581                             geometry: {
67582                                 type: 'Point',
67583                                 coordinates: coordPair(node).coordinates
67584                             }
67585                         };
67586                     }
67587                     function getLineStyle(extensions) {
67588                         var style = {};
67589                         if (extensions) {
67590                             var lineStyle = get1(extensions, 'line');
67591                             if (lineStyle) {
67592                                 var color = nodeVal(get1(lineStyle, 'color')),
67593                                     opacity = parseFloat(nodeVal(get1(lineStyle, 'opacity'))),
67594                                     width = parseFloat(nodeVal(get1(lineStyle, 'width')));
67595                                 if (color) { style.stroke = color; }
67596                                 if (!isNaN(opacity)) { style['stroke-opacity'] = opacity; }
67597                                 // GPX width is in mm, convert to px with 96 px per inch
67598                                 if (!isNaN(width)) { style['stroke-width'] = width * 96 / 25.4; }
67599                             }
67600                         }
67601                         return style;
67602                     }
67603                     function getProperties(node) {
67604                         var prop = getMulti(node, ['name', 'cmt', 'desc', 'type', 'time', 'keywords']),
67605                             links = get(node, 'link');
67606                         if (links.length) { prop.links = []; }
67607                         for (var i = 0, link; i < links.length; i++) {
67608                             link = { href: attr(links[i], 'href') };
67609                             extend(link, getMulti(links[i], ['text', 'type']));
67610                             prop.links.push(link);
67611                         }
67612                         return prop;
67613                     }
67614                     return gj;
67615                 }
67616             };
67617             return t;
67618         })();
67619
67620         { module.exports = toGeoJSON; }
67621         });
67622
67623         var _initialized = false;
67624         var _enabled = false;
67625         var _geojson;
67626
67627
67628         function svgData(projection, context, dispatch) {
67629             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
67630             var _showLabels = true;
67631             var detected = utilDetect();
67632             var layer = select(null);
67633             var _vtService;
67634             var _fileList;
67635             var _template;
67636             var _src;
67637
67638
67639             function init() {
67640                 if (_initialized) { return; }  // run once
67641
67642                 _geojson = {};
67643                 _enabled = true;
67644
67645                 function over() {
67646                     event.stopPropagation();
67647                     event.preventDefault();
67648                     event.dataTransfer.dropEffect = 'copy';
67649                 }
67650
67651                 context.container()
67652                     .attr('dropzone', 'copy')
67653                     .on('drop.svgData', function() {
67654                         event.stopPropagation();
67655                         event.preventDefault();
67656                         if (!detected.filedrop) { return; }
67657                         drawData.fileList(event.dataTransfer.files);
67658                     })
67659                     .on('dragenter.svgData', over)
67660                     .on('dragexit.svgData', over)
67661                     .on('dragover.svgData', over);
67662
67663                 _initialized = true;
67664             }
67665
67666
67667             function getService() {
67668                 if (services.vectorTile && !_vtService) {
67669                     _vtService = services.vectorTile;
67670                     _vtService.event.on('loadedData', throttledRedraw);
67671                 } else if (!services.vectorTile && _vtService) {
67672                     _vtService = null;
67673                 }
67674
67675                 return _vtService;
67676             }
67677
67678
67679             function showLayer() {
67680                 layerOn();
67681
67682                 layer
67683                     .style('opacity', 0)
67684                     .transition()
67685                     .duration(250)
67686                     .style('opacity', 1)
67687                     .on('end', function () { dispatch.call('change'); });
67688             }
67689
67690
67691             function hideLayer() {
67692                 throttledRedraw.cancel();
67693
67694                 layer
67695                     .transition()
67696                     .duration(250)
67697                     .style('opacity', 0)
67698                     .on('end', layerOff);
67699             }
67700
67701
67702             function layerOn() {
67703                 layer.style('display', 'block');
67704             }
67705
67706
67707             function layerOff() {
67708                 layer.selectAll('.viewfield-group').remove();
67709                 layer.style('display', 'none');
67710             }
67711
67712
67713             // ensure that all geojson features in a collection have IDs
67714             function ensureIDs(gj) {
67715                 if (!gj) { return null; }
67716
67717                 if (gj.type === 'FeatureCollection') {
67718                     for (var i = 0; i < gj.features.length; i++) {
67719                         ensureFeatureID(gj.features[i]);
67720                     }
67721                 } else {
67722                     ensureFeatureID(gj);
67723                 }
67724                 return gj;
67725             }
67726
67727
67728             // ensure that each single Feature object has a unique ID
67729             function ensureFeatureID(feature) {
67730                 if (!feature) { return; }
67731                 feature.__featurehash__ = utilHashcode(fastJsonStableStringify(feature));
67732                 return feature;
67733             }
67734
67735
67736             // Prefer an array of Features instead of a FeatureCollection
67737             function getFeatures(gj) {
67738                 if (!gj) { return []; }
67739
67740                 if (gj.type === 'FeatureCollection') {
67741                     return gj.features;
67742                 } else {
67743                     return [gj];
67744                 }
67745             }
67746
67747
67748             function featureKey(d) {
67749                 return d.__featurehash__;
67750             }
67751
67752
67753             function isPolygon(d) {
67754                 return d.geometry.type === 'Polygon' || d.geometry.type === 'MultiPolygon';
67755             }
67756
67757
67758             function clipPathID(d) {
67759                 return 'ideditor-data-' + d.__featurehash__ + '-clippath';
67760             }
67761
67762
67763             function featureClasses(d) {
67764                 return [
67765                     'data' + d.__featurehash__,
67766                     d.geometry.type,
67767                     isPolygon(d) ? 'area' : '',
67768                     d.__layerID__ || ''
67769                 ].filter(Boolean).join(' ');
67770             }
67771
67772
67773             function drawData(selection) {
67774                 var vtService = getService();
67775                 var getPath = svgPath(projection).geojson;
67776                 var getAreaPath = svgPath(projection, null, true).geojson;
67777                 var hasData = drawData.hasData();
67778
67779                 layer = selection.selectAll('.layer-mapdata')
67780                     .data(_enabled && hasData ? [0] : []);
67781
67782                 layer.exit()
67783                     .remove();
67784
67785                 layer = layer.enter()
67786                     .append('g')
67787                     .attr('class', 'layer-mapdata')
67788                     .merge(layer);
67789
67790                 var surface = context.surface();
67791                 if (!surface || surface.empty()) { return; }  // not ready to draw yet, starting up
67792
67793
67794                 // Gather data
67795                 var geoData, polygonData;
67796                 if (_template && vtService) {   // fetch data from vector tile service
67797                     var sourceID = _template;
67798                     vtService.loadTiles(sourceID, _template, projection);
67799                     geoData = vtService.data(sourceID, projection);
67800                 } else {
67801                     geoData = getFeatures(_geojson);
67802                 }
67803                 geoData = geoData.filter(getPath);
67804                 polygonData = geoData.filter(isPolygon);
67805
67806
67807                 // Draw clip paths for polygons
67808                 var clipPaths = surface.selectAll('defs').selectAll('.clipPath-data')
67809                    .data(polygonData, featureKey);
67810
67811                 clipPaths.exit()
67812                    .remove();
67813
67814                 var clipPathsEnter = clipPaths.enter()
67815                    .append('clipPath')
67816                    .attr('class', 'clipPath-data')
67817                    .attr('id', clipPathID);
67818
67819                 clipPathsEnter
67820                    .append('path');
67821
67822                 clipPaths.merge(clipPathsEnter)
67823                    .selectAll('path')
67824                    .attr('d', getAreaPath);
67825
67826
67827                 // Draw fill, shadow, stroke layers
67828                 var datagroups = layer
67829                     .selectAll('g.datagroup')
67830                     .data(['fill', 'shadow', 'stroke']);
67831
67832                 datagroups = datagroups.enter()
67833                     .append('g')
67834                     .attr('class', function(d) { return 'datagroup datagroup-' + d; })
67835                     .merge(datagroups);
67836
67837
67838                 // Draw paths
67839                 var pathData = {
67840                     fill: polygonData,
67841                     shadow: geoData,
67842                     stroke: geoData
67843                 };
67844
67845                 var paths = datagroups
67846                     .selectAll('path')
67847                     .data(function(layer) { return pathData[layer]; }, featureKey);
67848
67849                 // exit
67850                 paths.exit()
67851                     .remove();
67852
67853                 // enter/update
67854                 paths = paths.enter()
67855                     .append('path')
67856                     .attr('class', function(d) {
67857                         var datagroup = this.parentNode.__data__;
67858                         return 'pathdata ' + datagroup + ' ' + featureClasses(d);
67859                     })
67860                     .attr('clip-path', function(d) {
67861                         var datagroup = this.parentNode.__data__;
67862                         return datagroup === 'fill' ? ('url(#' + clipPathID(d) + ')') : null;
67863                     })
67864                     .merge(paths)
67865                     .attr('d', function(d) {
67866                         var datagroup = this.parentNode.__data__;
67867                         return datagroup === 'fill' ? getAreaPath(d) : getPath(d);
67868                     });
67869
67870
67871                 // Draw labels
67872                 layer
67873                     .call(drawLabels, 'label-halo', geoData)
67874                     .call(drawLabels, 'label', geoData);
67875
67876
67877                 function drawLabels(selection, textClass, data) {
67878                     var labelPath = d3_geoPath(projection);
67879                     var labelData = data.filter(function(d) {
67880                         return _showLabels && d.properties && (d.properties.desc || d.properties.name);
67881                     });
67882
67883                     var labels = selection.selectAll('text.' + textClass)
67884                         .data(labelData, featureKey);
67885
67886                     // exit
67887                     labels.exit()
67888                         .remove();
67889
67890                     // enter/update
67891                     labels = labels.enter()
67892                         .append('text')
67893                         .attr('class', function(d) { return textClass + ' ' + featureClasses(d); })
67894                         .merge(labels)
67895                         .text(function(d) {
67896                             return d.properties.desc || d.properties.name;
67897                         })
67898                         .attr('x', function(d) {
67899                             var centroid = labelPath.centroid(d);
67900                             return centroid[0] + 11;
67901                         })
67902                         .attr('y', function(d) {
67903                             var centroid = labelPath.centroid(d);
67904                             return centroid[1];
67905                         });
67906                 }
67907             }
67908
67909
67910             function getExtension(fileName) {
67911                 if (!fileName) { return; }
67912
67913                 var re = /\.(gpx|kml|(geo)?json)$/i;
67914                 var match = fileName.toLowerCase().match(re);
67915                 return match && match.length && match[0];
67916             }
67917
67918
67919             function xmlToDom(textdata) {
67920                 return (new DOMParser()).parseFromString(textdata, 'text/xml');
67921             }
67922
67923
67924             drawData.setFile = function(extension, data) {
67925                 _template = null;
67926                 _fileList = null;
67927                 _geojson = null;
67928                 _src = null;
67929
67930                 var gj;
67931                 switch (extension) {
67932                     case '.gpx':
67933                         gj = togeojson.gpx(xmlToDom(data));
67934                         break;
67935                     case '.kml':
67936                         gj = togeojson.kml(xmlToDom(data));
67937                         break;
67938                     case '.geojson':
67939                     case '.json':
67940                         gj = JSON.parse(data);
67941                         break;
67942                 }
67943
67944                 gj = gj || {};
67945                 if (Object.keys(gj).length) {
67946                     _geojson = ensureIDs(gj);
67947                     _src = extension + ' data file';
67948                     this.fitZoom();
67949                 }
67950
67951                 dispatch.call('change');
67952                 return this;
67953             };
67954
67955
67956             drawData.showLabels = function(val) {
67957                 if (!arguments.length) { return _showLabels; }
67958
67959                 _showLabels = val;
67960                 return this;
67961             };
67962
67963
67964             drawData.enabled = function(val) {
67965                 if (!arguments.length) { return _enabled; }
67966
67967                 _enabled = val;
67968                 if (_enabled) {
67969                     showLayer();
67970                 } else {
67971                     hideLayer();
67972                 }
67973
67974                 dispatch.call('change');
67975                 return this;
67976             };
67977
67978
67979             drawData.hasData = function() {
67980                 var gj = _geojson || {};
67981                 return !!(_template || Object.keys(gj).length);
67982             };
67983
67984
67985             drawData.template = function(val, src) {
67986                 if (!arguments.length) { return _template; }
67987
67988                 // test source against OSM imagery blacklists..
67989                 var osm = context.connection();
67990                 if (osm) {
67991                     var blacklists = osm.imageryBlacklists();
67992                     var fail = false;
67993                     var tested = 0;
67994                     var regex;
67995
67996                     for (var i = 0; i < blacklists.length; i++) {
67997                         try {
67998                             regex = new RegExp(blacklists[i]);
67999                             fail = regex.test(val);
68000                             tested++;
68001                             if (fail) { break; }
68002                         } catch (e) {
68003                             /* noop */
68004                         }
68005                     }
68006
68007                     // ensure at least one test was run.
68008                     if (!tested) {
68009                         regex = new RegExp('.*\.google(apis)?\..*/(vt|kh)[\?/].*([xyz]=.*){3}.*');
68010                         fail = regex.test(val);
68011                     }
68012                 }
68013
68014                 _template = val;
68015                 _fileList = null;
68016                 _geojson = null;
68017
68018                 // strip off the querystring/hash from the template,
68019                 // it often includes the access token
68020                 _src = src || ('vectortile:' + val.split(/[?#]/)[0]);
68021
68022                 dispatch.call('change');
68023                 return this;
68024             };
68025
68026
68027             drawData.geojson = function(gj, src) {
68028                 if (!arguments.length) { return _geojson; }
68029
68030                 _template = null;
68031                 _fileList = null;
68032                 _geojson = null;
68033                 _src = null;
68034
68035                 gj = gj || {};
68036                 if (Object.keys(gj).length) {
68037                     _geojson = ensureIDs(gj);
68038                     _src = src || 'unknown.geojson';
68039                 }
68040
68041                 dispatch.call('change');
68042                 return this;
68043             };
68044
68045
68046             drawData.fileList = function(fileList) {
68047                 if (!arguments.length) { return _fileList; }
68048
68049                 _template = null;
68050                 _fileList = fileList;
68051                 _geojson = null;
68052                 _src = null;
68053
68054                 if (!fileList || !fileList.length) { return this; }
68055                 var f = fileList[0];
68056                 var extension = getExtension(f.name);
68057                 var reader = new FileReader();
68058                 reader.onload = (function() {
68059                     return function(e) {
68060                         drawData.setFile(extension, e.target.result);
68061                     };
68062                 })();
68063
68064                 reader.readAsText(f);
68065
68066                 return this;
68067             };
68068
68069
68070             drawData.url = function(url, defaultExtension) {
68071                 _template = null;
68072                 _fileList = null;
68073                 _geojson = null;
68074                 _src = null;
68075
68076                 // strip off any querystring/hash from the url before checking extension
68077                 var testUrl = url.split(/[?#]/)[0];
68078                 var extension = getExtension(testUrl) || defaultExtension;
68079                 if (extension) {
68080                     _template = null;
68081                     d3_text(url)
68082                         .then(function(data) {
68083                             drawData.setFile(extension, data);
68084                         })
68085                         .catch(function() {
68086                             /* ignore */
68087                         });
68088
68089                 } else {
68090                     drawData.template(url);
68091                 }
68092
68093                 return this;
68094             };
68095
68096
68097             drawData.getSrc = function() {
68098                 return _src || '';
68099             };
68100
68101
68102             drawData.fitZoom = function() {
68103                 var features = getFeatures(_geojson);
68104                 if (!features.length) { return; }
68105
68106                 var map = context.map();
68107                 var viewport = map.trimmedExtent().polygon();
68108                 var coords = features.reduce(function(coords, feature) {
68109                     var geom = feature.geometry;
68110                     if (!geom) { return coords; }
68111
68112                     var c = geom.coordinates;
68113
68114                     /* eslint-disable no-fallthrough */
68115                     switch (geom.type) {
68116                         case 'Point':
68117                             c = [c];
68118                         case 'MultiPoint':
68119                         case 'LineString':
68120                             break;
68121
68122                         case 'MultiPolygon':
68123                             c = utilArrayFlatten(c);
68124                         case 'Polygon':
68125                         case 'MultiLineString':
68126                             c = utilArrayFlatten(c);
68127                             break;
68128                     }
68129                     /* eslint-enable no-fallthrough */
68130
68131                     return utilArrayUnion(coords, c);
68132                 }, []);
68133
68134                 if (!geoPolygonIntersectsPolygon(viewport, coords, true)) {
68135                     var extent = geoExtent(d3_geoBounds({ type: 'LineString', coordinates: coords }));
68136                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
68137                 }
68138
68139                 return this;
68140             };
68141
68142
68143             init();
68144             return drawData;
68145         }
68146
68147         function svgDebug(projection, context) {
68148
68149           function drawDebug(selection) {
68150             var showTile = context.getDebug('tile');
68151             var showCollision = context.getDebug('collision');
68152             var showImagery = context.getDebug('imagery');
68153             var showTouchTargets = context.getDebug('target');
68154             var showDownloaded = context.getDebug('downloaded');
68155
68156             var debugData = [];
68157             if (showTile) {
68158               debugData.push({ class: 'red', label: 'tile' });
68159             }
68160             if (showCollision) {
68161               debugData.push({ class: 'yellow', label: 'collision' });
68162             }
68163             if (showImagery) {
68164               debugData.push({ class: 'orange', label: 'imagery' });
68165             }
68166             if (showTouchTargets) {
68167               debugData.push({ class: 'pink', label: 'touchTargets' });
68168             }
68169             if (showDownloaded) {
68170               debugData.push({ class: 'purple', label: 'downloaded' });
68171             }
68172
68173
68174             var legend = context.container().select('.main-content')
68175               .selectAll('.debug-legend')
68176               .data(debugData.length ? [0] : []);
68177
68178             legend.exit()
68179               .remove();
68180
68181             legend = legend.enter()
68182               .append('div')
68183               .attr('class', 'fillD debug-legend')
68184               .merge(legend);
68185
68186
68187             var legendItems = legend.selectAll('.debug-legend-item')
68188               .data(debugData, function (d) { return d.label; });
68189
68190             legendItems.exit()
68191               .remove();
68192
68193             legendItems.enter()
68194               .append('span')
68195               .attr('class', function (d) { return ("debug-legend-item " + (d.class)); })
68196               .text(function (d) { return d.label; });
68197
68198
68199             var layer = selection.selectAll('.layer-debug')
68200               .data(showImagery || showDownloaded ? [0] : []);
68201
68202             layer.exit()
68203               .remove();
68204
68205             layer = layer.enter()
68206               .append('g')
68207               .attr('class', 'layer-debug')
68208               .merge(layer);
68209
68210
68211             // imagery
68212             var extent = context.map().extent();
68213             _mainFileFetcher.get('imagery')
68214               .then(function (d) {
68215                 var hits = (showImagery && d.query.bbox(extent.rectangle(), true)) || [];
68216                 var features = hits.map(function (d) { return d.features[d.id]; });
68217
68218                 var imagery = layer.selectAll('path.debug-imagery')
68219                   .data(features);
68220
68221                 imagery.exit()
68222                   .remove();
68223
68224                 imagery.enter()
68225                   .append('path')
68226                   .attr('class', 'debug-imagery debug orange');
68227               })
68228               .catch(function () { /* ignore */ });
68229
68230             // downloaded
68231             var osm = context.connection();
68232             var dataDownloaded = [];
68233             if (osm && showDownloaded) {
68234               var rtree = osm.caches('get').tile.rtree;
68235               dataDownloaded = rtree.all().map(function (bbox) {
68236                 return {
68237                   type: 'Feature',
68238                   properties: { id: bbox.id },
68239                   geometry: {
68240                     type: 'Polygon',
68241                     coordinates: [[
68242                       [ bbox.minX, bbox.minY ],
68243                       [ bbox.minX, bbox.maxY ],
68244                       [ bbox.maxX, bbox.maxY ],
68245                       [ bbox.maxX, bbox.minY ],
68246                       [ bbox.minX, bbox.minY ]
68247                     ]]
68248                   }
68249                 };
68250               });
68251             }
68252
68253             var downloaded = layer
68254               .selectAll('path.debug-downloaded')
68255               .data(showDownloaded ? dataDownloaded : []);
68256
68257             downloaded.exit()
68258               .remove();
68259
68260             downloaded.enter()
68261               .append('path')
68262               .attr('class', 'debug-downloaded debug purple');
68263
68264             // update
68265             layer.selectAll('path')
68266               .attr('d', svgPath(projection).geojson);
68267           }
68268
68269
68270           // This looks strange because `enabled` methods on other layers are
68271           // chainable getter/setters, and this one is just a getter.
68272           drawDebug.enabled = function() {
68273             if (!arguments.length) {
68274               return context.getDebug('tile') ||
68275                 context.getDebug('collision') ||
68276                 context.getDebug('imagery') ||
68277                 context.getDebug('target') ||
68278                 context.getDebug('downloaded');
68279             } else {
68280                 return this;
68281             }
68282           };
68283
68284
68285           return drawDebug;
68286         }
68287
68288         var _layerEnabled = false;
68289         var _qaService;
68290
68291         function svgKeepRight(projection, context, dispatch) {
68292           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
68293           var minZoom = 12;
68294
68295           var touchLayer = select(null);
68296           var drawLayer = select(null);
68297           var layerVisible = false;
68298
68299           function markerPath(selection, klass) {
68300             selection
68301               .attr('class', klass)
68302               .attr('transform', 'translate(-4, -24)')
68303               .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');
68304           }
68305
68306           // Loosely-coupled keepRight service for fetching issues.
68307           function getService() {
68308             if (services.keepRight && !_qaService) {
68309               _qaService = services.keepRight;
68310               _qaService.on('loaded', throttledRedraw);
68311             } else if (!services.keepRight && _qaService) {
68312               _qaService = null;
68313             }
68314
68315             return _qaService;
68316           }
68317
68318           // Show the markers
68319           function editOn() {
68320             if (!layerVisible) {
68321               layerVisible = true;
68322               drawLayer
68323                 .style('display', 'block');
68324             }
68325           }
68326
68327           // Immediately remove the markers and their touch targets
68328           function editOff() {
68329             if (layerVisible) {
68330               layerVisible = false;
68331               drawLayer
68332                 .style('display', 'none');
68333               drawLayer.selectAll('.qaItem.keepRight')
68334                 .remove();
68335               touchLayer.selectAll('.qaItem.keepRight')
68336                 .remove();
68337             }
68338           }
68339
68340           // Enable the layer.  This shows the markers and transitions them to visible.
68341           function layerOn() {
68342             editOn();
68343
68344             drawLayer
68345               .style('opacity', 0)
68346               .transition()
68347               .duration(250)
68348               .style('opacity', 1)
68349               .on('end interrupt', function () { return dispatch.call('change'); });
68350           }
68351
68352           // Disable the layer.  This transitions the layer invisible and then hides the markers.
68353           function layerOff() {
68354             throttledRedraw.cancel();
68355             drawLayer.interrupt();
68356             touchLayer.selectAll('.qaItem.keepRight')
68357               .remove();
68358
68359             drawLayer
68360               .transition()
68361               .duration(250)
68362               .style('opacity', 0)
68363               .on('end interrupt', function () {
68364                 editOff();
68365                 dispatch.call('change');
68366               });
68367           }
68368
68369           // Update the issue markers
68370           function updateMarkers() {
68371             if (!layerVisible || !_layerEnabled) { return; }
68372
68373             var service = getService();
68374             var selectedID = context.selectedErrorID();
68375             var data = (service ? service.getItems(projection) : []);
68376             var getTransform = svgPointTransform(projection);
68377
68378             // Draw markers..
68379             var markers = drawLayer.selectAll('.qaItem.keepRight')
68380               .data(data, function (d) { return d.id; });
68381
68382             // exit
68383             markers.exit()
68384               .remove();
68385
68386             // enter
68387             var markersEnter = markers.enter()
68388               .append('g')
68389                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.parentIssueType)); });
68390
68391             markersEnter
68392               .append('ellipse')
68393                 .attr('cx', 0.5)
68394                 .attr('cy', 1)
68395                 .attr('rx', 6.5)
68396                 .attr('ry', 3)
68397                 .attr('class', 'stroke');
68398
68399             markersEnter
68400               .append('path')
68401                 .call(markerPath, 'shadow');
68402
68403             markersEnter
68404               .append('use')
68405                 .attr('class', 'qaItem-fill')
68406                 .attr('width', '20px')
68407                 .attr('height', '20px')
68408                 .attr('x', '-8px')
68409                 .attr('y', '-22px')
68410                 .attr('xlink:href', '#iD-icon-bolt');
68411
68412             // update
68413             markers
68414               .merge(markersEnter)
68415               .sort(sortY)
68416                 .classed('selected', function (d) { return d.id === selectedID; })
68417                 .attr('transform', getTransform);
68418
68419
68420             // Draw targets..
68421             if (touchLayer.empty()) { return; }
68422             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
68423
68424             var targets = touchLayer.selectAll('.qaItem.keepRight')
68425               .data(data, function (d) { return d.id; });
68426
68427             // exit
68428             targets.exit()
68429               .remove();
68430
68431             // enter/update
68432             targets.enter()
68433               .append('rect')
68434                 .attr('width', '20px')
68435                 .attr('height', '20px')
68436                 .attr('x', '-8px')
68437                 .attr('y', '-22px')
68438               .merge(targets)
68439               .sort(sortY)
68440                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
68441                 .attr('transform', getTransform);
68442
68443
68444             function sortY(a, b) {
68445               return (a.id === selectedID) ? 1
68446                 : (b.id === selectedID) ? -1
68447                 : (a.severity === 'error' && b.severity !== 'error') ? 1
68448                 : (b.severity === 'error' && a.severity !== 'error') ? -1
68449                 : b.loc[1] - a.loc[1];
68450             }
68451           }
68452
68453           // Draw the keepRight layer and schedule loading issues and updating markers.
68454           function drawKeepRight(selection) {
68455             var service = getService();
68456
68457             var surface = context.surface();
68458             if (surface && !surface.empty()) {
68459               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
68460             }
68461
68462             drawLayer = selection.selectAll('.layer-keepRight')
68463               .data(service ? [0] : []);
68464
68465             drawLayer.exit()
68466               .remove();
68467
68468             drawLayer = drawLayer.enter()
68469               .append('g')
68470                 .attr('class', 'layer-keepRight')
68471                 .style('display', _layerEnabled ? 'block' : 'none')
68472               .merge(drawLayer);
68473
68474             if (_layerEnabled) {
68475               if (service && ~~context.map().zoom() >= minZoom) {
68476                 editOn();
68477                 service.loadIssues(projection);
68478                 updateMarkers();
68479               } else {
68480                 editOff();
68481               }
68482             }
68483           }
68484
68485           // Toggles the layer on and off
68486           drawKeepRight.enabled = function(val) {
68487             if (!arguments.length) { return _layerEnabled; }
68488
68489             _layerEnabled = val;
68490             if (_layerEnabled) {
68491               layerOn();
68492             } else {
68493               layerOff();
68494               if (context.selectedErrorID()) {
68495                 context.enter(modeBrowse(context));
68496               }
68497             }
68498
68499             dispatch.call('change');
68500             return this;
68501           };
68502
68503           drawKeepRight.supported = function () { return !!getService(); };
68504
68505           return drawKeepRight;
68506         }
68507
68508         function svgGeolocate(projection) {
68509             var layer = select(null);
68510             var _position;
68511
68512
68513             function init() {
68514                 if (svgGeolocate.initialized) { return; }  // run once
68515                 svgGeolocate.enabled = false;
68516                 svgGeolocate.initialized = true;
68517             }
68518
68519             function showLayer() {
68520                 layer.style('display', 'block');
68521             }
68522
68523
68524             function hideLayer() {
68525                 layer
68526                     .transition()
68527                     .duration(250)
68528                     .style('opacity', 0);
68529             }
68530
68531             function layerOn() {
68532                 layer
68533                     .style('opacity', 0)
68534                     .transition()
68535                     .duration(250)
68536                     .style('opacity', 1);
68537
68538             }
68539
68540             function layerOff() {
68541                 layer.style('display', 'none');
68542             }
68543
68544             function transform(d) {
68545                 return svgPointTransform(projection)(d);
68546             }
68547
68548             function accuracy(accuracy, loc) { // converts accuracy to pixels...
68549                 var degreesRadius = geoMetersToLat(accuracy),
68550                     tangentLoc = [loc[0], loc[1] + degreesRadius],
68551                     projectedTangent = projection(tangentLoc),
68552                     projectedLoc = projection([loc[0], loc[1]]);
68553
68554                 // southern most point will have higher pixel value...
68555                return Math.round(projectedLoc[1] - projectedTangent[1]).toString();
68556             }
68557
68558             function update() {
68559                 var geolocation = { loc: [_position.coords.longitude, _position.coords.latitude] };
68560
68561                 var groups = layer.selectAll('.geolocations').selectAll('.geolocation')
68562                     .data([geolocation]);
68563
68564                 groups.exit()
68565                     .remove();
68566
68567                 var pointsEnter = groups.enter()
68568                     .append('g')
68569                     .attr('class', 'geolocation');
68570
68571                 pointsEnter
68572                     .append('circle')
68573                     .attr('class', 'geolocate-radius')
68574                     .attr('dx', '0')
68575                     .attr('dy', '0')
68576                     .attr('fill', 'rgb(15,128,225)')
68577                     .attr('fill-opacity', '0.3')
68578                     .attr('r', '0');
68579
68580                 pointsEnter
68581                     .append('circle')
68582                     .attr('dx', '0')
68583                     .attr('dy', '0')
68584                     .attr('fill', 'rgb(15,128,225)')
68585                     .attr('stroke', 'white')
68586                     .attr('stroke-width', '1.5')
68587                     .attr('r', '6');
68588
68589                 groups.merge(pointsEnter)
68590                     .attr('transform', transform);
68591
68592                 layer.select('.geolocate-radius').attr('r', accuracy(_position.coords.accuracy, geolocation.loc));
68593             }
68594
68595             function drawLocation(selection) {
68596                 var enabled = svgGeolocate.enabled;
68597
68598                 layer = selection.selectAll('.layer-geolocate')
68599                     .data([0]);
68600
68601                 layer.exit()
68602                     .remove();
68603
68604                 var layerEnter = layer.enter()
68605                     .append('g')
68606                     .attr('class', 'layer-geolocate')
68607                     .style('display', enabled ? 'block' : 'none');
68608
68609                 layerEnter
68610                     .append('g')
68611                     .attr('class', 'geolocations');
68612
68613                 layer = layerEnter
68614                     .merge(layer);
68615
68616                 if (enabled) {
68617                     update();
68618                 } else {
68619                     layerOff();
68620                 }
68621             }
68622
68623             drawLocation.enabled = function (position, enabled) {
68624                 if (!arguments.length) { return svgGeolocate.enabled; }
68625                 _position = position;
68626                 svgGeolocate.enabled = enabled;
68627                 if (svgGeolocate.enabled) {
68628                     showLayer();
68629                     layerOn();
68630                 } else {
68631                     hideLayer();
68632                 }
68633                 return this;
68634             };
68635
68636             init();
68637             return drawLocation;
68638         }
68639
68640         function svgLabels(projection, context) {
68641             var path = d3_geoPath(projection);
68642             var detected = utilDetect();
68643             var baselineHack = (detected.ie ||
68644                 detected.browser.toLowerCase() === 'edge' ||
68645                 (detected.browser.toLowerCase() === 'firefox' && detected.version >= 70));
68646             var _rdrawn = new RBush();
68647             var _rskipped = new RBush();
68648             var _textWidthCache = {};
68649             var _entitybboxes = {};
68650
68651             // Listed from highest to lowest priority
68652             var labelStack = [
68653                 ['line', 'aeroway', '*', 12],
68654                 ['line', 'highway', 'motorway', 12],
68655                 ['line', 'highway', 'trunk', 12],
68656                 ['line', 'highway', 'primary', 12],
68657                 ['line', 'highway', 'secondary', 12],
68658                 ['line', 'highway', 'tertiary', 12],
68659                 ['line', 'highway', '*', 12],
68660                 ['line', 'railway', '*', 12],
68661                 ['line', 'waterway', '*', 12],
68662                 ['area', 'aeroway', '*', 12],
68663                 ['area', 'amenity', '*', 12],
68664                 ['area', 'building', '*', 12],
68665                 ['area', 'historic', '*', 12],
68666                 ['area', 'leisure', '*', 12],
68667                 ['area', 'man_made', '*', 12],
68668                 ['area', 'natural', '*', 12],
68669                 ['area', 'shop', '*', 12],
68670                 ['area', 'tourism', '*', 12],
68671                 ['area', 'camp_site', '*', 12],
68672                 ['point', 'aeroway', '*', 10],
68673                 ['point', 'amenity', '*', 10],
68674                 ['point', 'building', '*', 10],
68675                 ['point', 'historic', '*', 10],
68676                 ['point', 'leisure', '*', 10],
68677                 ['point', 'man_made', '*', 10],
68678                 ['point', 'natural', '*', 10],
68679                 ['point', 'shop', '*', 10],
68680                 ['point', 'tourism', '*', 10],
68681                 ['point', 'camp_site', '*', 10],
68682                 ['line', 'name', '*', 12],
68683                 ['area', 'name', '*', 12],
68684                 ['point', 'name', '*', 10]
68685             ];
68686
68687
68688             function shouldSkipIcon(preset) {
68689                 var noIcons = ['building', 'landuse', 'natural'];
68690                 return noIcons.some(function(s) {
68691                     return preset.id.indexOf(s) >= 0;
68692                 });
68693             }
68694
68695
68696             function get(array, prop) {
68697                 return function(d, i) { return array[i][prop]; };
68698             }
68699
68700
68701             function textWidth(text, size, elem) {
68702                 var c = _textWidthCache[size];
68703                 if (!c) { c = _textWidthCache[size] = {}; }
68704
68705                 if (c[text]) {
68706                     return c[text];
68707
68708                 } else if (elem) {
68709                     c[text] = elem.getComputedTextLength();
68710                     return c[text];
68711
68712                 } else {
68713                     var str = encodeURIComponent(text).match(/%[CDEFcdef]/g);
68714                     if (str === null) {
68715                         return size / 3 * 2 * text.length;
68716                     } else {
68717                         return size / 3 * (2 * text.length + str.length);
68718                     }
68719                 }
68720             }
68721
68722
68723             function drawLinePaths(selection, entities, filter, classes, labels) {
68724                 var paths = selection.selectAll('path')
68725                     .filter(filter)
68726                     .data(entities, osmEntity.key);
68727
68728                 // exit
68729                 paths.exit()
68730                     .remove();
68731
68732                 // enter/update
68733                 paths.enter()
68734                     .append('path')
68735                     .style('stroke-width', get(labels, 'font-size'))
68736                     .attr('id', function(d) { return 'ideditor-labelpath-' + d.id; })
68737                     .attr('class', classes)
68738                     .merge(paths)
68739                     .attr('d', get(labels, 'lineString'));
68740             }
68741
68742
68743             function drawLineLabels(selection, entities, filter, classes, labels) {
68744                 var texts = selection.selectAll('text.' + classes)
68745                     .filter(filter)
68746                     .data(entities, osmEntity.key);
68747
68748                 // exit
68749                 texts.exit()
68750                     .remove();
68751
68752                 // enter
68753                 texts.enter()
68754                     .append('text')
68755                     .attr('class', function(d, i) { return classes + ' ' + labels[i].classes + ' ' + d.id; })
68756                     .attr('dy', baselineHack ? '0.35em' : null)
68757                     .append('textPath')
68758                     .attr('class', 'textpath');
68759
68760                 // update
68761                 selection.selectAll('text.' + classes).selectAll('.textpath')
68762                     .filter(filter)
68763                     .data(entities, osmEntity.key)
68764                     .attr('startOffset', '50%')
68765                     .attr('xlink:href', function(d) { return '#ideditor-labelpath-' + d.id; })
68766                     .text(utilDisplayNameForPath);
68767             }
68768
68769
68770             function drawPointLabels(selection, entities, filter, classes, labels) {
68771                 var texts = selection.selectAll('text.' + classes)
68772                     .filter(filter)
68773                     .data(entities, osmEntity.key);
68774
68775                 // exit
68776                 texts.exit()
68777                     .remove();
68778
68779                 // enter/update
68780                 texts.enter()
68781                     .append('text')
68782                     .attr('class', function(d, i) {
68783                         return classes + ' ' + labels[i].classes + ' ' + d.id;
68784                     })
68785                     .merge(texts)
68786                     .attr('x', get(labels, 'x'))
68787                     .attr('y', get(labels, 'y'))
68788                     .style('text-anchor', get(labels, 'textAnchor'))
68789                     .text(utilDisplayName)
68790                     .each(function(d, i) {
68791                         textWidth(utilDisplayName(d), labels[i].height, this);
68792                     });
68793             }
68794
68795
68796             function drawAreaLabels(selection, entities, filter, classes, labels) {
68797                 entities = entities.filter(hasText);
68798                 labels = labels.filter(hasText);
68799                 drawPointLabels(selection, entities, filter, classes, labels);
68800
68801                 function hasText(d, i) {
68802                     return labels[i].hasOwnProperty('x') && labels[i].hasOwnProperty('y');
68803                 }
68804             }
68805
68806
68807             function drawAreaIcons(selection, entities, filter, classes, labels) {
68808                 var icons = selection.selectAll('use.' + classes)
68809                     .filter(filter)
68810                     .data(entities, osmEntity.key);
68811
68812                 // exit
68813                 icons.exit()
68814                     .remove();
68815
68816                 // enter/update
68817                 icons.enter()
68818                     .append('use')
68819                     .attr('class', 'icon ' + classes)
68820                     .attr('width', '17px')
68821                     .attr('height', '17px')
68822                     .merge(icons)
68823                     .attr('transform', get(labels, 'transform'))
68824                     .attr('xlink:href', function(d) {
68825                         var preset = _mainPresetIndex.match(d, context.graph());
68826                         var picon = preset && preset.icon;
68827
68828                         if (!picon) {
68829                             return '';
68830                         } else {
68831                             var isMaki = /^maki-/.test(picon);
68832                             return '#' + picon + (isMaki ? '-15' : '');
68833                         }
68834                     });
68835             }
68836
68837
68838             function drawCollisionBoxes(selection, rtree, which) {
68839                 var classes = 'debug ' + which + ' ' + (which === 'debug-skipped' ? 'orange' : 'yellow');
68840
68841                 var gj = [];
68842                 if (context.getDebug('collision')) {
68843                     gj = rtree.all().map(function(d) {
68844                         return { type: 'Polygon', coordinates: [[
68845                             [d.minX, d.minY],
68846                             [d.maxX, d.minY],
68847                             [d.maxX, d.maxY],
68848                             [d.minX, d.maxY],
68849                             [d.minX, d.minY]
68850                         ]]};
68851                     });
68852                 }
68853
68854                 var boxes = selection.selectAll('.' + which)
68855                     .data(gj);
68856
68857                 // exit
68858                 boxes.exit()
68859                     .remove();
68860
68861                 // enter/update
68862                 boxes.enter()
68863                     .append('path')
68864                     .attr('class', classes)
68865                     .merge(boxes)
68866                     .attr('d', d3_geoPath());
68867             }
68868
68869
68870             function drawLabels(selection, graph, entities, filter, dimensions, fullRedraw) {
68871                 var wireframe = context.surface().classed('fill-wireframe');
68872                 var zoom = geoScaleToZoom(projection.scale());
68873
68874                 var labelable = [];
68875                 var renderNodeAs = {};
68876                 var i, j, k, entity, geometry;
68877
68878                 for (i = 0; i < labelStack.length; i++) {
68879                     labelable.push([]);
68880                 }
68881
68882                 if (fullRedraw) {
68883                     _rdrawn.clear();
68884                     _rskipped.clear();
68885                     _entitybboxes = {};
68886
68887                 } else {
68888                     for (i = 0; i < entities.length; i++) {
68889                         entity = entities[i];
68890                         var toRemove = []
68891                             .concat(_entitybboxes[entity.id] || [])
68892                             .concat(_entitybboxes[entity.id + 'I'] || []);
68893
68894                         for (j = 0; j < toRemove.length; j++) {
68895                             _rdrawn.remove(toRemove[j]);
68896                             _rskipped.remove(toRemove[j]);
68897                         }
68898                     }
68899                 }
68900
68901                 // Loop through all the entities to do some preprocessing
68902                 for (i = 0; i < entities.length; i++) {
68903                     entity = entities[i];
68904                     geometry = entity.geometry(graph);
68905
68906                     // Insert collision boxes around interesting points/vertices
68907                     if (geometry === 'point' || (geometry === 'vertex' && isInterestingVertex(entity))) {
68908                         var hasDirections = entity.directions(graph, projection).length;
68909                         var markerPadding;
68910
68911                         if (!wireframe && geometry === 'point' && !(zoom >= 18 && hasDirections)) {
68912                             renderNodeAs[entity.id] = 'point';
68913                             markerPadding = 20;   // extra y for marker height
68914                         } else {
68915                             renderNodeAs[entity.id] = 'vertex';
68916                             markerPadding = 0;
68917                         }
68918
68919                         var coord = projection(entity.loc);
68920                         var nodePadding = 10;
68921                         var bbox = {
68922                             minX: coord[0] - nodePadding,
68923                             minY: coord[1] - nodePadding - markerPadding,
68924                             maxX: coord[0] + nodePadding,
68925                             maxY: coord[1] + nodePadding
68926                         };
68927
68928                         doInsert(bbox, entity.id + 'P');
68929                     }
68930
68931                     // From here on, treat vertices like points
68932                     if (geometry === 'vertex') {
68933                         geometry = 'point';
68934                     }
68935
68936                     // Determine which entities are label-able
68937                     var preset = geometry === 'area' && _mainPresetIndex.match(entity, graph);
68938                     var icon = preset && !shouldSkipIcon(preset) && preset.icon;
68939
68940                     if (!icon && !utilDisplayName(entity))
68941                         { continue; }
68942
68943                     for (k = 0; k < labelStack.length; k++) {
68944                         var matchGeom = labelStack[k][0];
68945                         var matchKey = labelStack[k][1];
68946                         var matchVal = labelStack[k][2];
68947                         var hasVal = entity.tags[matchKey];
68948
68949                         if (geometry === matchGeom && hasVal && (matchVal === '*' || matchVal === hasVal)) {
68950                             labelable[k].push(entity);
68951                             break;
68952                         }
68953                     }
68954                 }
68955
68956                 var positions = {
68957                     point: [],
68958                     line: [],
68959                     area: []
68960                 };
68961
68962                 var labelled = {
68963                     point: [],
68964                     line: [],
68965                     area: []
68966                 };
68967
68968                 // Try and find a valid label for labellable entities
68969                 for (k = 0; k < labelable.length; k++) {
68970                     var fontSize = labelStack[k][3];
68971
68972                     for (i = 0; i < labelable[k].length; i++) {
68973                         entity = labelable[k][i];
68974                         geometry = entity.geometry(graph);
68975
68976                         var getName = (geometry === 'line') ? utilDisplayNameForPath : utilDisplayName;
68977                         var name = getName(entity);
68978                         var width = name && textWidth(name, fontSize);
68979                         var p = null;
68980
68981                         if (geometry === 'point' || geometry === 'vertex') {
68982                             // no point or vertex labels in wireframe mode
68983                             // no vertex labels at low zooms (vertices have no icons)
68984                             if (wireframe) { continue; }
68985                             var renderAs = renderNodeAs[entity.id];
68986                             if (renderAs === 'vertex' && zoom < 17) { continue; }
68987
68988                             p = getPointLabel(entity, width, fontSize, renderAs);
68989
68990                         } else if (geometry === 'line') {
68991                             p = getLineLabel(entity, width, fontSize);
68992
68993                         } else if (geometry === 'area') {
68994                             p = getAreaLabel(entity, width, fontSize);
68995                         }
68996
68997                         if (p) {
68998                             if (geometry === 'vertex') { geometry = 'point'; }  // treat vertex like point
68999                             p.classes = geometry + ' tag-' + labelStack[k][1];
69000                             positions[geometry].push(p);
69001                             labelled[geometry].push(entity);
69002                         }
69003                     }
69004                 }
69005
69006
69007                 function isInterestingVertex(entity) {
69008                     var selectedIDs = context.selectedIDs();
69009
69010                     return entity.hasInterestingTags() ||
69011                         entity.isEndpoint(graph) ||
69012                         entity.isConnected(graph) ||
69013                         selectedIDs.indexOf(entity.id) !== -1 ||
69014                         graph.parentWays(entity).some(function(parent) {
69015                             return selectedIDs.indexOf(parent.id) !== -1;
69016                         });
69017                 }
69018
69019
69020                 function getPointLabel(entity, width, height, geometry) {
69021                     var y = (geometry === 'point' ? -12 : 0);
69022                     var pointOffsets = {
69023                         ltr: [15, y, 'start'],
69024                         rtl: [-15, y, 'end']
69025                     };
69026
69027                     var textDirection = _mainLocalizer.textDirection();
69028
69029                     var coord = projection(entity.loc);
69030                     var textPadding = 2;
69031                     var offset = pointOffsets[textDirection];
69032                     var p = {
69033                         height: height,
69034                         width: width,
69035                         x: coord[0] + offset[0],
69036                         y: coord[1] + offset[1],
69037                         textAnchor: offset[2]
69038                     };
69039
69040                     // insert a collision box for the text label..
69041                     var bbox;
69042                     if (textDirection === 'rtl') {
69043                         bbox = {
69044                             minX: p.x - width - textPadding,
69045                             minY: p.y - (height / 2) - textPadding,
69046                             maxX: p.x + textPadding,
69047                             maxY: p.y + (height / 2) + textPadding
69048                         };
69049                     } else {
69050                         bbox = {
69051                             minX: p.x - textPadding,
69052                             minY: p.y - (height / 2) - textPadding,
69053                             maxX: p.x + width + textPadding,
69054                             maxY: p.y + (height / 2) + textPadding
69055                         };
69056                     }
69057
69058                     if (tryInsert([bbox], entity.id, true)) {
69059                         return p;
69060                     }
69061                 }
69062
69063
69064                 function getLineLabel(entity, width, height) {
69065                     var viewport = geoExtent(context.projection.clipExtent()).polygon();
69066                     var points = graph.childNodes(entity)
69067                         .map(function(node) { return projection(node.loc); });
69068                     var length = geoPathLength(points);
69069
69070                     if (length < width + 20) { return; }
69071
69072                     // % along the line to attempt to place the label
69073                     var lineOffsets = [50, 45, 55, 40, 60, 35, 65, 30, 70,
69074                                        25, 75, 20, 80, 15, 95, 10, 90, 5, 95];
69075                     var padding = 3;
69076
69077                     for (var i = 0; i < lineOffsets.length; i++) {
69078                         var offset = lineOffsets[i];
69079                         var middle = offset / 100 * length;
69080                         var start = middle - width / 2;
69081
69082                         if (start < 0 || start + width > length) { continue; }
69083
69084                         // generate subpath and ignore paths that are invalid or don't cross viewport.
69085                         var sub = subpath(points, start, start + width);
69086                         if (!sub || !geoPolygonIntersectsPolygon(viewport, sub, true)) {
69087                             continue;
69088                         }
69089
69090                         var isReverse = reverse(sub);
69091                         if (isReverse) {
69092                             sub = sub.reverse();
69093                         }
69094
69095                         var bboxes = [];
69096                         var boxsize = (height + 2) / 2;
69097
69098                         for (var j = 0; j < sub.length - 1; j++) {
69099                             var a = sub[j];
69100                             var b = sub[j + 1];
69101
69102                             // split up the text into small collision boxes
69103                             var num = Math.max(1, Math.floor(geoVecLength(a, b) / boxsize / 2));
69104
69105                             for (var box = 0; box < num; box++) {
69106                                 var p = geoVecInterp(a, b, box / num);
69107                                 var x0 = p[0] - boxsize - padding;
69108                                 var y0 = p[1] - boxsize - padding;
69109                                 var x1 = p[0] + boxsize + padding;
69110                                 var y1 = p[1] + boxsize + padding;
69111
69112                                 bboxes.push({
69113                                     minX: Math.min(x0, x1),
69114                                     minY: Math.min(y0, y1),
69115                                     maxX: Math.max(x0, x1),
69116                                     maxY: Math.max(y0, y1)
69117                                 });
69118                             }
69119                         }
69120
69121                         if (tryInsert(bboxes, entity.id, false)) {   // accept this one
69122                             return {
69123                                 'font-size': height + 2,
69124                                 lineString: lineString(sub),
69125                                 startOffset: offset + '%'
69126                             };
69127                         }
69128                     }
69129
69130                     function reverse(p) {
69131                         var angle = Math.atan2(p[1][1] - p[0][1], p[1][0] - p[0][0]);
69132                         return !(p[0][0] < p[p.length - 1][0] && angle < Math.PI/2 && angle > -Math.PI/2);
69133                     }
69134
69135                     function lineString(points) {
69136                         return 'M' + points.join('L');
69137                     }
69138
69139                     function subpath(points, from, to) {
69140                         var sofar = 0;
69141                         var start, end, i0, i1;
69142
69143                         for (var i = 0; i < points.length - 1; i++) {
69144                             var a = points[i];
69145                             var b = points[i + 1];
69146                             var current = geoVecLength(a, b);
69147                             var portion;
69148                             if (!start && sofar + current >= from) {
69149                                 portion = (from - sofar) / current;
69150                                 start = [
69151                                     a[0] + portion * (b[0] - a[0]),
69152                                     a[1] + portion * (b[1] - a[1])
69153                                 ];
69154                                 i0 = i + 1;
69155                             }
69156                             if (!end && sofar + current >= to) {
69157                                 portion = (to - sofar) / current;
69158                                 end = [
69159                                     a[0] + portion * (b[0] - a[0]),
69160                                     a[1] + portion * (b[1] - a[1])
69161                                 ];
69162                                 i1 = i + 1;
69163                             }
69164                             sofar += current;
69165                         }
69166
69167                         var result = points.slice(i0, i1);
69168                         result.unshift(start);
69169                         result.push(end);
69170                         return result;
69171                     }
69172                 }
69173
69174
69175                 function getAreaLabel(entity, width, height) {
69176                     var centroid = path.centroid(entity.asGeoJSON(graph, true));
69177                     var extent = entity.extent(graph);
69178                     var areaWidth = projection(extent[1])[0] - projection(extent[0])[0];
69179
69180                     if (isNaN(centroid[0]) || areaWidth < 20) { return; }
69181
69182                     var preset = _mainPresetIndex.match(entity, context.graph());
69183                     var picon = preset && preset.icon;
69184                     var iconSize = 17;
69185                     var padding = 2;
69186                     var p = {};
69187
69188                     if (picon) {  // icon and label..
69189                         if (addIcon()) {
69190                             addLabel(iconSize + padding);
69191                             return p;
69192                         }
69193                     } else {   // label only..
69194                         if (addLabel(0)) {
69195                             return p;
69196                         }
69197                     }
69198
69199
69200                     function addIcon() {
69201                         var iconX = centroid[0] - (iconSize / 2);
69202                         var iconY = centroid[1] - (iconSize / 2);
69203                         var bbox = {
69204                             minX: iconX,
69205                             minY: iconY,
69206                             maxX: iconX + iconSize,
69207                             maxY: iconY + iconSize
69208                         };
69209
69210                         if (tryInsert([bbox], entity.id + 'I', true)) {
69211                             p.transform = 'translate(' + iconX + ',' + iconY + ')';
69212                             return true;
69213                         }
69214                         return false;
69215                     }
69216
69217                     function addLabel(yOffset) {
69218                         if (width && areaWidth >= width + 20) {
69219                             var labelX = centroid[0];
69220                             var labelY = centroid[1] + yOffset;
69221                             var bbox = {
69222                                 minX: labelX - (width / 2) - padding,
69223                                 minY: labelY - (height / 2) - padding,
69224                                 maxX: labelX + (width / 2) + padding,
69225                                 maxY: labelY + (height / 2) + padding
69226                             };
69227
69228                             if (tryInsert([bbox], entity.id, true)) {
69229                                 p.x = labelX;
69230                                 p.y = labelY;
69231                                 p.textAnchor = 'middle';
69232                                 p.height = height;
69233                                 return true;
69234                             }
69235                         }
69236                         return false;
69237                     }
69238                 }
69239
69240
69241                 // force insert a singular bounding box
69242                 // singular box only, no array, id better be unique
69243                 function doInsert(bbox, id) {
69244                     bbox.id = id;
69245
69246                     var oldbox = _entitybboxes[id];
69247                     if (oldbox) {
69248                         _rdrawn.remove(oldbox);
69249                     }
69250                     _entitybboxes[id] = bbox;
69251                     _rdrawn.insert(bbox);
69252                 }
69253
69254
69255                 function tryInsert(bboxes, id, saveSkipped) {
69256                     var skipped = false;
69257
69258                     for (var i = 0; i < bboxes.length; i++) {
69259                         var bbox = bboxes[i];
69260                         bbox.id = id;
69261
69262                         // Check that label is visible
69263                         if (bbox.minX < 0 || bbox.minY < 0 || bbox.maxX > dimensions[0] || bbox.maxY > dimensions[1]) {
69264                             skipped = true;
69265                             break;
69266                         }
69267                         if (_rdrawn.collides(bbox)) {
69268                             skipped = true;
69269                             break;
69270                         }
69271                     }
69272
69273                     _entitybboxes[id] = bboxes;
69274
69275                     if (skipped) {
69276                         if (saveSkipped) {
69277                             _rskipped.load(bboxes);
69278                         }
69279                     } else {
69280                         _rdrawn.load(bboxes);
69281                     }
69282
69283                     return !skipped;
69284                 }
69285
69286
69287                 var layer = selection.selectAll('.layer-osm.labels');
69288                 layer.selectAll('.labels-group')
69289                     .data(['halo', 'label', 'debug'])
69290                     .enter()
69291                     .append('g')
69292                     .attr('class', function(d) { return 'labels-group ' + d; });
69293
69294                 var halo = layer.selectAll('.labels-group.halo');
69295                 var label = layer.selectAll('.labels-group.label');
69296                 var debug = layer.selectAll('.labels-group.debug');
69297
69298                 // points
69299                 drawPointLabels(label, labelled.point, filter, 'pointlabel', positions.point);
69300                 drawPointLabels(halo, labelled.point, filter, 'pointlabel-halo', positions.point);
69301
69302                 // lines
69303                 drawLinePaths(layer, labelled.line, filter, '', positions.line);
69304                 drawLineLabels(label, labelled.line, filter, 'linelabel', positions.line);
69305                 drawLineLabels(halo, labelled.line, filter, 'linelabel-halo', positions.line);
69306
69307                 // areas
69308                 drawAreaLabels(label, labelled.area, filter, 'arealabel', positions.area);
69309                 drawAreaLabels(halo, labelled.area, filter, 'arealabel-halo', positions.area);
69310                 drawAreaIcons(label, labelled.area, filter, 'areaicon', positions.area);
69311                 drawAreaIcons(halo, labelled.area, filter, 'areaicon-halo', positions.area);
69312
69313                 // debug
69314                 drawCollisionBoxes(debug, _rskipped, 'debug-skipped');
69315                 drawCollisionBoxes(debug, _rdrawn, 'debug-drawn');
69316
69317                 layer.call(filterLabels);
69318             }
69319
69320
69321             function filterLabels(selection) {
69322                 var drawLayer = selection.selectAll('.layer-osm.labels');
69323                 var layers = drawLayer.selectAll('.labels-group.halo, .labels-group.label');
69324
69325                 layers.selectAll('.nolabel')
69326                     .classed('nolabel', false);
69327
69328                 var mouse = context.map().mouse();
69329                 var graph = context.graph();
69330                 var selectedIDs = context.selectedIDs();
69331                 var ids = [];
69332                 var pad, bbox;
69333
69334                 // hide labels near the mouse
69335                 if (mouse) {
69336                     pad = 20;
69337                     bbox = { minX: mouse[0] - pad, minY: mouse[1] - pad, maxX: mouse[0] + pad, maxY: mouse[1] + pad };
69338                     var nearMouse = _rdrawn.search(bbox).map(function(entity) { return entity.id; });
69339                     ids.push.apply(ids, nearMouse);
69340                 }
69341
69342                 // hide labels on selected nodes (they look weird when dragging / haloed)
69343                 for (var i = 0; i < selectedIDs.length; i++) {
69344                     var entity = graph.hasEntity(selectedIDs[i]);
69345                     if (entity && entity.type === 'node') {
69346                         ids.push(selectedIDs[i]);
69347                     }
69348                 }
69349
69350                 layers.selectAll(utilEntitySelector(ids))
69351                     .classed('nolabel', true);
69352
69353
69354                 // draw the mouse bbox if debugging is on..
69355                 var debug = selection.selectAll('.labels-group.debug');
69356                 var gj = [];
69357                 if (context.getDebug('collision')) {
69358                     gj = bbox ? [{
69359                         type: 'Polygon',
69360                         coordinates: [[
69361                             [bbox.minX, bbox.minY],
69362                             [bbox.maxX, bbox.minY],
69363                             [bbox.maxX, bbox.maxY],
69364                             [bbox.minX, bbox.maxY],
69365                             [bbox.minX, bbox.minY]
69366                         ]]
69367                     }] : [];
69368                 }
69369
69370                 var box = debug.selectAll('.debug-mouse')
69371                     .data(gj);
69372
69373                 // exit
69374                 box.exit()
69375                     .remove();
69376
69377                 // enter/update
69378                 box.enter()
69379                     .append('path')
69380                     .attr('class', 'debug debug-mouse yellow')
69381                     .merge(box)
69382                     .attr('d', d3_geoPath());
69383             }
69384
69385
69386             var throttleFilterLabels = throttle(filterLabels, 100);
69387
69388
69389             drawLabels.observe = function(selection) {
69390                 var listener = function() { throttleFilterLabels(selection); };
69391                 selection.on('mousemove.hidelabels', listener);
69392                 context.on('enter.hidelabels', listener);
69393             };
69394
69395
69396             drawLabels.off = function(selection) {
69397                 throttleFilterLabels.cancel();
69398                 selection.on('mousemove.hidelabels', null);
69399                 context.on('enter.hidelabels', null);
69400             };
69401
69402
69403             return drawLabels;
69404         }
69405
69406         var _layerEnabled$1 = false;
69407         var _qaService$1;
69408
69409         function svgImproveOSM(projection, context, dispatch) {
69410           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69411           var minZoom = 12;
69412
69413           var touchLayer = select(null);
69414           var drawLayer = select(null);
69415           var layerVisible = false;
69416
69417           function markerPath(selection, klass) {
69418             selection
69419               .attr('class', klass)
69420               .attr('transform', 'translate(-10, -28)')
69421               .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');
69422           }
69423
69424           // Loosely-coupled improveOSM service for fetching issues
69425           function getService() {
69426             if (services.improveOSM && !_qaService$1) {
69427               _qaService$1 = services.improveOSM;
69428               _qaService$1.on('loaded', throttledRedraw);
69429             } else if (!services.improveOSM && _qaService$1) {
69430               _qaService$1 = null;
69431             }
69432
69433             return _qaService$1;
69434           }
69435
69436           // Show the markers
69437           function editOn() {
69438             if (!layerVisible) {
69439               layerVisible = true;
69440               drawLayer
69441                 .style('display', 'block');
69442             }
69443           }
69444
69445           // Immediately remove the markers and their touch targets
69446           function editOff() {
69447             if (layerVisible) {
69448               layerVisible = false;
69449               drawLayer
69450                 .style('display', 'none');
69451               drawLayer.selectAll('.qaItem.improveOSM')
69452                 .remove();
69453               touchLayer.selectAll('.qaItem.improveOSM')
69454                 .remove();
69455             }
69456           }
69457
69458           // Enable the layer.  This shows the markers and transitions them to visible.
69459           function layerOn() {
69460             editOn();
69461
69462             drawLayer
69463               .style('opacity', 0)
69464               .transition()
69465               .duration(250)
69466               .style('opacity', 1)
69467               .on('end interrupt', function () { return dispatch.call('change'); });
69468           }
69469
69470           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69471           function layerOff() {
69472             throttledRedraw.cancel();
69473             drawLayer.interrupt();
69474             touchLayer.selectAll('.qaItem.improveOSM')
69475               .remove();
69476
69477             drawLayer
69478               .transition()
69479               .duration(250)
69480               .style('opacity', 0)
69481               .on('end interrupt', function () {
69482                 editOff();
69483                 dispatch.call('change');
69484               });
69485           }
69486
69487           // Update the issue markers
69488           function updateMarkers() {
69489             if (!layerVisible || !_layerEnabled$1) { return; }
69490
69491             var service = getService();
69492             var selectedID = context.selectedErrorID();
69493             var data = (service ? service.getItems(projection) : []);
69494             var getTransform = svgPointTransform(projection);
69495
69496             // Draw markers..
69497             var markers = drawLayer.selectAll('.qaItem.improveOSM')
69498               .data(data, function (d) { return d.id; });
69499
69500             // exit
69501             markers.exit()
69502               .remove();
69503
69504             // enter
69505             var markersEnter = markers.enter()
69506               .append('g')
69507                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69508
69509             markersEnter
69510               .append('polygon')
69511                 .call(markerPath, 'shadow');
69512
69513             markersEnter
69514               .append('ellipse')
69515                 .attr('cx', 0)
69516                 .attr('cy', 0)
69517                 .attr('rx', 4.5)
69518                 .attr('ry', 2)
69519                 .attr('class', 'stroke');
69520
69521             markersEnter
69522               .append('polygon')
69523                 .attr('fill', 'currentColor')
69524                 .call(markerPath, 'qaItem-fill');
69525
69526             markersEnter
69527               .append('use')
69528                 .attr('transform', 'translate(-6.5, -23)')
69529                 .attr('class', 'icon-annotation')
69530                 .attr('width', '13px')
69531                 .attr('height', '13px')
69532                 .attr('xlink:href', function (d) {
69533                   var picon = d.icon;
69534
69535                   if (!picon) {
69536                   return '';
69537                   } else {
69538                   var isMaki = /^maki-/.test(picon);
69539                   return ("#" + picon + (isMaki ? '-11' : ''));
69540                   }
69541                 });
69542
69543             // update
69544             markers
69545               .merge(markersEnter)
69546               .sort(sortY)
69547                 .classed('selected', function (d) { return d.id === selectedID; })
69548                 .attr('transform', getTransform);
69549
69550
69551             // Draw targets..
69552             if (touchLayer.empty()) { return; }
69553             var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
69554
69555             var targets = touchLayer.selectAll('.qaItem.improveOSM')
69556               .data(data, function (d) { return d.id; });
69557
69558             // exit
69559             targets.exit()
69560               .remove();
69561
69562             // enter/update
69563             targets.enter()
69564               .append('rect')
69565                 .attr('width', '20px')
69566                 .attr('height', '30px')
69567                 .attr('x', '-10px')
69568                 .attr('y', '-28px')
69569               .merge(targets)
69570               .sort(sortY)
69571                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69572                 .attr('transform', getTransform);
69573
69574             function sortY(a, b) {
69575               return (a.id === selectedID) ? 1
69576                 : (b.id === selectedID) ? -1
69577                 : b.loc[1] - a.loc[1];
69578             }
69579           }
69580
69581           // Draw the ImproveOSM layer and schedule loading issues and updating markers.
69582           function drawImproveOSM(selection) {
69583             var service = getService();
69584
69585             var surface = context.surface();
69586             if (surface && !surface.empty()) {
69587               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69588             }
69589
69590             drawLayer = selection.selectAll('.layer-improveOSM')
69591               .data(service ? [0] : []);
69592
69593             drawLayer.exit()
69594               .remove();
69595
69596             drawLayer = drawLayer.enter()
69597               .append('g')
69598                 .attr('class', 'layer-improveOSM')
69599                 .style('display', _layerEnabled$1 ? 'block' : 'none')
69600               .merge(drawLayer);
69601
69602             if (_layerEnabled$1) {
69603               if (service && ~~context.map().zoom() >= minZoom) {
69604                 editOn();
69605                 service.loadIssues(projection);
69606                 updateMarkers();
69607               } else {
69608                 editOff();
69609               }
69610             }
69611           }
69612
69613           // Toggles the layer on and off
69614           drawImproveOSM.enabled = function(val) {
69615             if (!arguments.length) { return _layerEnabled$1; }
69616
69617             _layerEnabled$1 = val;
69618             if (_layerEnabled$1) {
69619               layerOn();
69620             } else {
69621               layerOff();
69622               if (context.selectedErrorID()) {
69623                 context.enter(modeBrowse(context));
69624               }
69625             }
69626
69627             dispatch.call('change');
69628             return this;
69629           };
69630
69631           drawImproveOSM.supported = function () { return !!getService(); };
69632
69633           return drawImproveOSM;
69634         }
69635
69636         var _layerEnabled$2 = false;
69637         var _qaService$2;
69638
69639         function svgOsmose(projection, context, dispatch) {
69640           var throttledRedraw = throttle(function () { return dispatch.call('change'); }, 1000);
69641           var minZoom = 12;
69642
69643           var touchLayer = select(null);
69644           var drawLayer = select(null);
69645           var layerVisible = false;
69646
69647           function markerPath(selection, klass) {
69648             selection
69649               .attr('class', klass)
69650               .attr('transform', 'translate(-10, -28)')
69651               .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');
69652           }
69653
69654           // Loosely-coupled osmose service for fetching issues
69655           function getService() {
69656             if (services.osmose && !_qaService$2) {
69657               _qaService$2 = services.osmose;
69658               _qaService$2.on('loaded', throttledRedraw);
69659             } else if (!services.osmose && _qaService$2) {
69660               _qaService$2 = null;
69661             }
69662
69663             return _qaService$2;
69664           }
69665
69666           // Show the markers
69667           function editOn() {
69668             if (!layerVisible) {
69669               layerVisible = true;
69670               drawLayer
69671                 .style('display', 'block');
69672             }
69673           }
69674
69675           // Immediately remove the markers and their touch targets
69676           function editOff() {
69677             if (layerVisible) {
69678               layerVisible = false;
69679               drawLayer
69680                 .style('display', 'none');
69681               drawLayer.selectAll('.qaItem.osmose')
69682                 .remove();
69683               touchLayer.selectAll('.qaItem.osmose')
69684                 .remove();
69685             }
69686           }
69687
69688           // Enable the layer.  This shows the markers and transitions them to visible.
69689           function layerOn() {
69690             editOn();
69691
69692             drawLayer
69693               .style('opacity', 0)
69694               .transition()
69695               .duration(250)
69696               .style('opacity', 1)
69697               .on('end interrupt', function () { return dispatch.call('change'); });
69698           }
69699
69700           // Disable the layer.  This transitions the layer invisible and then hides the markers.
69701           function layerOff() {
69702             throttledRedraw.cancel();
69703             drawLayer.interrupt();
69704             touchLayer.selectAll('.qaItem.osmose')
69705               .remove();
69706
69707             drawLayer
69708               .transition()
69709               .duration(250)
69710               .style('opacity', 0)
69711               .on('end interrupt', function () {
69712                 editOff();
69713                 dispatch.call('change');
69714               });
69715           }
69716
69717           // Update the issue markers
69718           function updateMarkers() {
69719             if (!layerVisible || !_layerEnabled$2) { return; }
69720
69721             var service = getService();
69722             var selectedID = context.selectedErrorID();
69723             var data = (service ? service.getItems(projection) : []);
69724             var getTransform = svgPointTransform(projection);
69725
69726             // Draw markers..
69727             var markers = drawLayer.selectAll('.qaItem.osmose')
69728               .data(data, function (d) { return d.id; });
69729
69730             // exit
69731             markers.exit()
69732               .remove();
69733
69734             // enter
69735             var markersEnter = markers.enter()
69736               .append('g')
69737                 .attr('class', function (d) { return ("qaItem " + (d.service) + " itemId-" + (d.id) + " itemType-" + (d.itemType)); });
69738
69739             markersEnter
69740               .append('polygon')
69741                 .call(markerPath, 'shadow');
69742
69743             markersEnter
69744               .append('ellipse')
69745                 .attr('cx', 0)
69746                 .attr('cy', 0)
69747                 .attr('rx', 4.5)
69748                 .attr('ry', 2)
69749                 .attr('class', 'stroke');
69750
69751             markersEnter
69752               .append('polygon')
69753                 .attr('fill', function (d) { return service.getColor(d.item); })
69754                 .call(markerPath, 'qaItem-fill');
69755
69756             markersEnter
69757               .append('use')
69758                 .attr('transform', 'translate(-6.5, -23)')
69759                 .attr('class', 'icon-annotation')
69760                 .attr('width', '13px')
69761                 .attr('height', '13px')
69762                 .attr('xlink:href', function (d) {
69763                   var picon = d.icon;
69764
69765                   if (!picon) {
69766                     return '';
69767                   } else {
69768                     var isMaki = /^maki-/.test(picon);
69769                     return ("#" + picon + (isMaki ? '-11' : ''));
69770                   }
69771                 });
69772
69773             // update
69774             markers
69775               .merge(markersEnter)
69776               .sort(sortY)
69777                 .classed('selected', function (d) { return d.id === selectedID; })
69778                 .attr('transform', getTransform);
69779
69780             // Draw targets..
69781             if (touchLayer.empty()) { return; }
69782             var fillClass = context.getDebug('target') ? 'pink' : 'nocolor';
69783
69784             var targets = touchLayer.selectAll('.qaItem.osmose')
69785               .data(data, function (d) { return d.id; });
69786
69787             // exit
69788             targets.exit()
69789               .remove();
69790
69791             // enter/update
69792             targets.enter()
69793               .append('rect')
69794                 .attr('width', '20px')
69795                 .attr('height', '30px')
69796                 .attr('x', '-10px')
69797                 .attr('y', '-28px')
69798               .merge(targets)
69799               .sort(sortY)
69800                 .attr('class', function (d) { return ("qaItem " + (d.service) + " target " + fillClass + " itemId-" + (d.id)); })
69801                 .attr('transform', getTransform);
69802
69803             function sortY(a, b) {
69804               return (a.id === selectedID) ? 1
69805                 : (b.id === selectedID) ? -1
69806                 : b.loc[1] - a.loc[1];
69807             }
69808           }
69809
69810           // Draw the Osmose layer and schedule loading issues and updating markers.
69811           function drawOsmose(selection) {
69812             var service = getService();
69813
69814             var surface = context.surface();
69815             if (surface && !surface.empty()) {
69816               touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
69817             }
69818
69819             drawLayer = selection.selectAll('.layer-osmose')
69820               .data(service ? [0] : []);
69821
69822             drawLayer.exit()
69823               .remove();
69824
69825             drawLayer = drawLayer.enter()
69826               .append('g')
69827                 .attr('class', 'layer-osmose')
69828                 .style('display', _layerEnabled$2 ? 'block' : 'none')
69829               .merge(drawLayer);
69830
69831             if (_layerEnabled$2) {
69832               if (service && ~~context.map().zoom() >= minZoom) {
69833                 editOn();
69834                 service.loadIssues(projection);
69835                 updateMarkers();
69836               } else {
69837                 editOff();
69838               }
69839             }
69840           }
69841
69842           // Toggles the layer on and off
69843           drawOsmose.enabled = function(val) {
69844             if (!arguments.length) { return _layerEnabled$2; }
69845
69846             _layerEnabled$2 = val;
69847             if (_layerEnabled$2) {
69848               // Strings supplied by Osmose fetched before showing layer for first time
69849               // NOTE: Currently no way to change locale in iD at runtime, would need to re-call this method if that's ever implemented
69850               // Also, If layer is toggled quickly multiple requests are sent
69851               getService().loadStrings()
69852                 .then(layerOn)
69853                 .catch(function (err) {
69854                   console.log(err); // eslint-disable-line no-console
69855                 });
69856             } else {
69857               layerOff();
69858               if (context.selectedErrorID()) {
69859                 context.enter(modeBrowse(context));
69860               }
69861             }
69862
69863             dispatch.call('change');
69864             return this;
69865           };
69866
69867           drawOsmose.supported = function () { return !!getService(); };
69868
69869           return drawOsmose;
69870         }
69871
69872         function svgStreetside(projection, context, dispatch) {
69873             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
69874             var minZoom = 14;
69875             var minMarkerZoom = 16;
69876             var minViewfieldZoom = 18;
69877             var layer = select(null);
69878             var _viewerYaw = 0;
69879             var _selectedSequence = null;
69880             var _streetside;
69881
69882             /**
69883              * init().
69884              */
69885             function init() {
69886                 if (svgStreetside.initialized) { return; }  // run once
69887                 svgStreetside.enabled = false;
69888                 svgStreetside.initialized = true;
69889             }
69890
69891             /**
69892              * getService().
69893              */
69894             function getService() {
69895                 if (services.streetside && !_streetside) {
69896                     _streetside = services.streetside;
69897                     _streetside.event
69898                         .on('viewerChanged.svgStreetside', viewerChanged)
69899                         .on('loadedBubbles.svgStreetside', throttledRedraw);
69900                 } else if (!services.streetside && _streetside) {
69901                     _streetside = null;
69902                 }
69903
69904                 return _streetside;
69905             }
69906
69907             /**
69908              * showLayer().
69909              */
69910             function showLayer() {
69911                 var service = getService();
69912                 if (!service) { return; }
69913
69914                 editOn();
69915
69916                 layer
69917                     .style('opacity', 0)
69918                     .transition()
69919                     .duration(250)
69920                     .style('opacity', 1)
69921                     .on('end', function () { dispatch.call('change'); });
69922             }
69923
69924             /**
69925              * hideLayer().
69926              */
69927             function hideLayer() {
69928                 throttledRedraw.cancel();
69929
69930                 layer
69931                     .transition()
69932                     .duration(250)
69933                     .style('opacity', 0)
69934                     .on('end', editOff);
69935             }
69936
69937             /**
69938              * editOn().
69939              */
69940             function editOn() {
69941                 layer.style('display', 'block');
69942             }
69943
69944             /**
69945              * editOff().
69946              */
69947             function editOff() {
69948                 layer.selectAll('.viewfield-group').remove();
69949                 layer.style('display', 'none');
69950             }
69951
69952             /**
69953              * click() Handles 'bubble' point click event.
69954              */
69955             function click(d) {
69956                 var service = getService();
69957                 if (!service) { return; }
69958
69959                 // try to preserve the viewer rotation when staying on the same sequence
69960                 if (d.sequenceKey !== _selectedSequence) {
69961                     _viewerYaw = 0;  // reset
69962                 }
69963                 _selectedSequence = d.sequenceKey;
69964
69965                 service
69966                     .selectImage(context, d)
69967                     .then(function (response) {
69968                         if (response.status === 'ok'){
69969                             service.showViewer(context, _viewerYaw);
69970                         }
69971                     });
69972
69973
69974                 context.map().centerEase(d.loc);
69975             }
69976
69977             /**
69978              * mouseover().
69979              */
69980             function mouseover(d) {
69981                 var service = getService();
69982                 if (service) { service.setStyles(context, d); }
69983             }
69984
69985             /**
69986              * mouseout().
69987              */
69988             function mouseout() {
69989                 var service = getService();
69990                 if (service) { service.setStyles(context, null); }
69991             }
69992
69993             /**
69994              * transform().
69995              */
69996             function transform(d) {
69997                 var t = svgPointTransform(projection)(d);
69998                 var rot = d.ca + _viewerYaw;
69999                 if (rot) {
70000                     t += ' rotate(' + Math.floor(rot) + ',0,0)';
70001                 }
70002                 return t;
70003             }
70004
70005
70006             function viewerChanged() {
70007                 var service = getService();
70008                 if (!service) { return; }
70009
70010                 var viewer = service.viewer();
70011                 if (!viewer) { return; }
70012
70013                 // update viewfield rotation
70014                 _viewerYaw = viewer.getYaw();
70015
70016                 // avoid updating if the map is currently transformed
70017                 // e.g. during drags or easing.
70018                 if (context.map().isTransformed()) { return; }
70019
70020                 layer.selectAll('.viewfield-group.currentView')
70021                     .attr('transform', transform);
70022             }
70023
70024
70025             context.photos().on('change.streetside', update);
70026
70027             /**
70028              * update().
70029              */
70030             function update() {
70031                 var viewer = context.container().select('.photoviewer');
70032                 var selected = viewer.empty() ? undefined : viewer.datum();
70033                 var z = ~~context.map().zoom();
70034                 var showMarkers = (z >= minMarkerZoom);
70035                 var showViewfields = (z >= minViewfieldZoom);
70036                 var service = getService();
70037
70038                 var sequences = [];
70039                 var bubbles = [];
70040
70041                 if (context.photos().showsPanoramic()) {
70042                     sequences = (service ? service.sequences(projection) : []);
70043                     bubbles = (service && showMarkers ? service.bubbles(projection) : []);
70044                 }
70045
70046                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70047                     .data(sequences, function(d) { return d.properties.key; });
70048
70049                 // exit
70050                 traces.exit()
70051                     .remove();
70052
70053                 // enter/update
70054                 traces = traces.enter()
70055                     .append('path')
70056                     .attr('class', 'sequence')
70057                     .merge(traces)
70058                     .attr('d', svgPath(projection).geojson);
70059
70060
70061                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70062                     .data(bubbles, function(d) {
70063                         // force reenter once bubbles are attached to a sequence
70064                         return d.key + (d.sequenceKey ? 'v1' : 'v0');
70065                     });
70066
70067                 // exit
70068                 groups.exit()
70069                     .remove();
70070
70071                 // enter
70072                 var groupsEnter = groups.enter()
70073                     .append('g')
70074                     .attr('class', 'viewfield-group')
70075                     .on('mouseenter', mouseover)
70076                     .on('mouseleave', mouseout)
70077                     .on('click', click);
70078
70079                 groupsEnter
70080                     .append('g')
70081                     .attr('class', 'viewfield-scale');
70082
70083                 // update
70084                 var markers = groups
70085                     .merge(groupsEnter)
70086                     .sort(function(a, b) {
70087                         return (a === selected) ? 1
70088                             : (b === selected) ? -1
70089                             : b.loc[1] - a.loc[1];
70090                     })
70091                     .attr('transform', transform)
70092                     .select('.viewfield-scale');
70093
70094
70095                 markers.selectAll('circle')
70096                     .data([0])
70097                     .enter()
70098                     .append('circle')
70099                     .attr('dx', '0')
70100                     .attr('dy', '0')
70101                     .attr('r', '6');
70102
70103                 var viewfields = markers.selectAll('.viewfield')
70104                     .data(showViewfields ? [0] : []);
70105
70106                 viewfields.exit()
70107                     .remove();
70108
70109                 // viewfields may or may not be drawn...
70110                 // but if they are, draw below the circles
70111                 viewfields.enter()
70112                     .insert('path', 'circle')
70113                     .attr('class', 'viewfield')
70114                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70115                     .attr('d', viewfieldPath);
70116
70117                 function viewfieldPath() {
70118                     var d = this.parentNode.__data__;
70119                     if (d.pano) {
70120                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70121                     } else {
70122                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70123                     }
70124                 }
70125
70126             }
70127
70128             /**
70129              * drawImages()
70130              * drawImages is the method that is returned (and that runs) everytime 'svgStreetside()' is called.
70131              * 'svgStreetside()' is called from index.js
70132              */
70133             function drawImages(selection) {
70134                 var enabled = svgStreetside.enabled;
70135                 var service = getService();
70136
70137                 layer = selection.selectAll('.layer-streetside-images')
70138                     .data(service ? [0] : []);
70139
70140                 layer.exit()
70141                     .remove();
70142
70143                 var layerEnter = layer.enter()
70144                     .append('g')
70145                     .attr('class', 'layer-streetside-images')
70146                     .style('display', enabled ? 'block' : 'none');
70147
70148                 layerEnter
70149                     .append('g')
70150                     .attr('class', 'sequences');
70151
70152                 layerEnter
70153                     .append('g')
70154                     .attr('class', 'markers');
70155
70156                 layer = layerEnter
70157                     .merge(layer);
70158
70159                 if (enabled) {
70160                     if (service && ~~context.map().zoom() >= minZoom) {
70161                         editOn();
70162                         update();
70163                         service.loadBubbles(projection);
70164                     } else {
70165                         editOff();
70166                     }
70167                 }
70168             }
70169
70170
70171             /**
70172              * drawImages.enabled().
70173              */
70174             drawImages.enabled = function(_) {
70175                 if (!arguments.length) { return svgStreetside.enabled; }
70176                 svgStreetside.enabled = _;
70177                 if (svgStreetside.enabled) {
70178                     showLayer();
70179                 } else {
70180                     hideLayer();
70181                 }
70182                 dispatch.call('change');
70183                 return this;
70184             };
70185
70186             /**
70187              * drawImages.supported().
70188              */
70189             drawImages.supported = function() {
70190                 return !!getService();
70191             };
70192
70193             init();
70194
70195             return drawImages;
70196         }
70197
70198         function svgMapillaryImages(projection, context, dispatch) {
70199             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70200             var minZoom = 12;
70201             var minMarkerZoom = 16;
70202             var minViewfieldZoom = 18;
70203             var layer = select(null);
70204             var _mapillary;
70205             var viewerCompassAngle;
70206
70207
70208             function init() {
70209                 if (svgMapillaryImages.initialized) { return; }  // run once
70210                 svgMapillaryImages.enabled = false;
70211                 svgMapillaryImages.initialized = true;
70212             }
70213
70214
70215             function getService() {
70216                 if (services.mapillary && !_mapillary) {
70217                     _mapillary = services.mapillary;
70218                     _mapillary.event.on('loadedImages', throttledRedraw);
70219                     _mapillary.event.on('bearingChanged', function(e) {
70220                         viewerCompassAngle = e;
70221
70222                         // avoid updating if the map is currently transformed
70223                         // e.g. during drags or easing.
70224                         if (context.map().isTransformed()) { return; }
70225
70226                         layer.selectAll('.viewfield-group.currentView')
70227                             .filter(function(d) {
70228                                 return d.pano;
70229                             })
70230                             .attr('transform', transform);
70231                     });
70232                 } else if (!services.mapillary && _mapillary) {
70233                     _mapillary = null;
70234                 }
70235
70236                 return _mapillary;
70237             }
70238
70239
70240             function showLayer() {
70241                 var service = getService();
70242                 if (!service) { return; }
70243
70244                 editOn();
70245
70246                 layer
70247                     .style('opacity', 0)
70248                     .transition()
70249                     .duration(250)
70250                     .style('opacity', 1)
70251                     .on('end', function () { dispatch.call('change'); });
70252             }
70253
70254
70255             function hideLayer() {
70256                 throttledRedraw.cancel();
70257
70258                 layer
70259                     .transition()
70260                     .duration(250)
70261                     .style('opacity', 0)
70262                     .on('end', editOff);
70263             }
70264
70265
70266             function editOn() {
70267                 layer.style('display', 'block');
70268             }
70269
70270
70271             function editOff() {
70272                 layer.selectAll('.viewfield-group').remove();
70273                 layer.style('display', 'none');
70274             }
70275
70276
70277             function click(d) {
70278                 var service = getService();
70279                 if (!service) { return; }
70280
70281                 service
70282                     .selectImage(context, d.key)
70283                     .updateViewer(context, d.key)
70284                     .showViewer(context);
70285
70286                 context.map().centerEase(d.loc);
70287             }
70288
70289
70290             function mouseover(d) {
70291                 var service = getService();
70292                 if (service) { service.setStyles(context, d); }
70293             }
70294
70295
70296             function mouseout() {
70297                 var service = getService();
70298                 if (service) { service.setStyles(context, null); }
70299             }
70300
70301
70302             function transform(d) {
70303                 var t = svgPointTransform(projection)(d);
70304                 if (d.pano && viewerCompassAngle !== null && isFinite(viewerCompassAngle)) {
70305                     t += ' rotate(' + Math.floor(viewerCompassAngle) + ',0,0)';
70306                 } else if (d.ca) {
70307                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70308                 }
70309                 return t;
70310             }
70311
70312             context.photos().on('change.mapillary_images', update);
70313
70314             function filterImages(images) {
70315                 var showsPano = context.photos().showsPanoramic();
70316                 var showsFlat = context.photos().showsFlat();
70317                 if (!showsPano || !showsFlat) {
70318                     images = images.filter(function(image) {
70319                         if (image.pano) { return showsPano; }
70320                         return showsFlat;
70321                     });
70322                 }
70323                 return images;
70324             }
70325
70326             function filterSequences(sequences, service) {
70327                 var showsPano = context.photos().showsPanoramic();
70328                 var showsFlat = context.photos().showsFlat();
70329                 if (!showsPano || !showsFlat) {
70330                     sequences = sequences.filter(function(sequence) {
70331                         if (sequence.properties.hasOwnProperty('pano')) {
70332                             if (sequence.properties.pano) { return showsPano; }
70333                             return showsFlat;
70334                         } else {
70335                             // if the sequence doesn't specify pano or not, search its images
70336                             var cProps = sequence.properties.coordinateProperties;
70337                             if (cProps && cProps.image_keys && cProps.image_keys.length > 0) {
70338                                 for (var index in cProps.image_keys) {
70339                                     var imageKey = cProps.image_keys[index];
70340                                     var image = service.cachedImage(imageKey);
70341                                     if (image && image.hasOwnProperty('pano')) {
70342                                         if (image.pano) { return showsPano; }
70343                                         return showsFlat;
70344                                     }
70345                                 }
70346                             }
70347                         }
70348                     });
70349                 }
70350                 return sequences;
70351             }
70352
70353             function update() {
70354
70355                 var z = ~~context.map().zoom();
70356                 var showMarkers = (z >= minMarkerZoom);
70357                 var showViewfields = (z >= minViewfieldZoom);
70358
70359                 var service = getService();
70360                 var selectedKey = service && service.getSelectedImageKey();
70361                 var sequences = (service ? service.sequences(projection) : []);
70362                 var images = (service && showMarkers ? service.images(projection) : []);
70363
70364                 images = filterImages(images);
70365                 sequences = filterSequences(sequences, service);
70366
70367                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
70368                     .data(sequences, function(d) { return d.properties.key; });
70369
70370                 // exit
70371                 traces.exit()
70372                     .remove();
70373
70374                 // enter/update
70375                 traces = traces.enter()
70376                     .append('path')
70377                     .attr('class', 'sequence')
70378                     .merge(traces)
70379                     .attr('d', svgPath(projection).geojson);
70380
70381
70382                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
70383                     .data(images, function(d) { return d.key; });
70384
70385                 // exit
70386                 groups.exit()
70387                     .remove();
70388
70389                 // enter
70390                 var groupsEnter = groups.enter()
70391                     .append('g')
70392                     .attr('class', 'viewfield-group')
70393                     .on('mouseenter', mouseover)
70394                     .on('mouseleave', mouseout)
70395                     .on('click', click);
70396
70397                 groupsEnter
70398                     .append('g')
70399                     .attr('class', 'viewfield-scale');
70400
70401                 // update
70402                 var markers = groups
70403                     .merge(groupsEnter)
70404                     .sort(function(a, b) {
70405                         return (a.key === selectedKey) ? 1
70406                             : (b.key === selectedKey) ? -1
70407                             : b.loc[1] - a.loc[1];  // sort Y
70408                     })
70409                     .attr('transform', transform)
70410                     .select('.viewfield-scale');
70411
70412
70413                 markers.selectAll('circle')
70414                     .data([0])
70415                     .enter()
70416                     .append('circle')
70417                     .attr('dx', '0')
70418                     .attr('dy', '0')
70419                     .attr('r', '6');
70420
70421                 var viewfields = markers.selectAll('.viewfield')
70422                     .data(showViewfields ? [0] : []);
70423
70424                 viewfields.exit()
70425                     .remove();
70426
70427                 viewfields.enter()               // viewfields may or may not be drawn...
70428                     .insert('path', 'circle')    // but if they are, draw below the circles
70429                     .attr('class', 'viewfield')
70430                     .classed('pano', function() { return this.parentNode.__data__.pano; })
70431                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
70432                     .attr('d', viewfieldPath);
70433
70434                 function viewfieldPath() {
70435                     var d = this.parentNode.__data__;
70436                     if (d.pano) {
70437                         return 'M 8,13 m -10,0 a 10,10 0 1,0 20,0 a 10,10 0 1,0 -20,0';
70438                     } else {
70439                         return 'M 6,9 C 8,8.4 8,8.4 10,9 L 16,-2 C 12,-5 4,-5 0,-2 z';
70440                     }
70441                 }
70442             }
70443
70444
70445             function drawImages(selection) {
70446                 var enabled = svgMapillaryImages.enabled;
70447                 var service = getService();
70448
70449                 layer = selection.selectAll('.layer-mapillary')
70450                     .data(service ? [0] : []);
70451
70452                 layer.exit()
70453                     .remove();
70454
70455                 var layerEnter = layer.enter()
70456                     .append('g')
70457                     .attr('class', 'layer-mapillary')
70458                     .style('display', enabled ? 'block' : 'none');
70459
70460                 layerEnter
70461                     .append('g')
70462                     .attr('class', 'sequences');
70463
70464                 layerEnter
70465                     .append('g')
70466                     .attr('class', 'markers');
70467
70468                 layer = layerEnter
70469                     .merge(layer);
70470
70471                 if (enabled) {
70472                     if (service && ~~context.map().zoom() >= minZoom) {
70473                         editOn();
70474                         update();
70475                         service.loadImages(projection);
70476                     } else {
70477                         editOff();
70478                     }
70479                 }
70480             }
70481
70482
70483             drawImages.enabled = function(_) {
70484                 if (!arguments.length) { return svgMapillaryImages.enabled; }
70485                 svgMapillaryImages.enabled = _;
70486                 if (svgMapillaryImages.enabled) {
70487                     showLayer();
70488                 } else {
70489                     hideLayer();
70490                 }
70491                 dispatch.call('change');
70492                 return this;
70493             };
70494
70495
70496             drawImages.supported = function() {
70497                 return !!getService();
70498             };
70499
70500
70501             init();
70502             return drawImages;
70503         }
70504
70505         function svgMapillarySigns(projection, context, dispatch) {
70506             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70507             var minZoom = 12;
70508             var layer = select(null);
70509             var _mapillary;
70510
70511
70512             function init() {
70513                 if (svgMapillarySigns.initialized) { return; }  // run once
70514                 svgMapillarySigns.enabled = false;
70515                 svgMapillarySigns.initialized = true;
70516             }
70517
70518
70519             function getService() {
70520                 if (services.mapillary && !_mapillary) {
70521                     _mapillary = services.mapillary;
70522                     _mapillary.event.on('loadedSigns', throttledRedraw);
70523                 } else if (!services.mapillary && _mapillary) {
70524                     _mapillary = null;
70525                 }
70526                 return _mapillary;
70527             }
70528
70529
70530             function showLayer() {
70531                 var service = getService();
70532                 if (!service) { return; }
70533
70534                 editOn();
70535             }
70536
70537
70538             function hideLayer() {
70539                 throttledRedraw.cancel();
70540                 editOff();
70541             }
70542
70543
70544             function editOn() {
70545                 layer.style('display', 'block');
70546             }
70547
70548
70549             function editOff() {
70550                 layer.selectAll('.icon-sign').remove();
70551                 layer.style('display', 'none');
70552             }
70553
70554
70555             function click(d) {
70556                 var service = getService();
70557                 if (!service) { return; }
70558
70559                 context.map().centerEase(d.loc);
70560
70561                 var selectedImageKey = service.getSelectedImageKey();
70562                 var imageKey;
70563
70564                 // Pick one of the images the sign was detected in,
70565                 // preference given to an image already selected.
70566                 d.detections.forEach(function(detection) {
70567                     if (!imageKey || selectedImageKey === detection.image_key) {
70568                         imageKey = detection.image_key;
70569                     }
70570                 });
70571
70572                 service
70573                     .selectImage(context, imageKey)
70574                     .updateViewer(context, imageKey)
70575                     .showViewer(context);
70576             }
70577
70578
70579             function update() {
70580                 var service = getService();
70581                 var data = (service ? service.signs(projection) : []);
70582                 var selectedImageKey = service.getSelectedImageKey();
70583                 var transform = svgPointTransform(projection);
70584
70585                 var signs = layer.selectAll('.icon-sign')
70586                     .data(data, function(d) { return d.key; });
70587
70588                 // exit
70589                 signs.exit()
70590                     .remove();
70591
70592                 // enter
70593                 var enter = signs.enter()
70594                     .append('g')
70595                     .attr('class', 'icon-sign icon-detected')
70596                     .on('click', click);
70597
70598                 enter
70599                     .append('use')
70600                     .attr('width', '24px')
70601                     .attr('height', '24px')
70602                     .attr('x', '-12px')
70603                     .attr('y', '-12px')
70604                     .attr('xlink:href', function(d) { return '#' + d.value; });
70605
70606                 enter
70607                     .append('rect')
70608                     .attr('width', '24px')
70609                     .attr('height', '24px')
70610                     .attr('x', '-12px')
70611                     .attr('y', '-12px');
70612
70613                 // update
70614                 signs
70615                     .merge(enter)
70616                     .attr('transform', transform)
70617                     .classed('currentView', function(d) {
70618                         return d.detections.some(function(detection) {
70619                             return detection.image_key === selectedImageKey;
70620                         });
70621                     })
70622                     .sort(function(a, b) {
70623                         var aSelected = a.detections.some(function(detection) {
70624                             return detection.image_key === selectedImageKey;
70625                         });
70626                         var bSelected = b.detections.some(function(detection) {
70627                             return detection.image_key === selectedImageKey;
70628                         });
70629                         if (aSelected === bSelected) {
70630                             return b.loc[1] - a.loc[1]; // sort Y
70631                         } else if (aSelected) {
70632                             return 1;
70633                         }
70634                         return -1;
70635                     });
70636             }
70637
70638
70639             function drawSigns(selection) {
70640                 var enabled = svgMapillarySigns.enabled;
70641                 var service = getService();
70642
70643                 layer = selection.selectAll('.layer-mapillary-signs')
70644                     .data(service ? [0] : []);
70645
70646                 layer.exit()
70647                     .remove();
70648
70649                 layer = layer.enter()
70650                     .append('g')
70651                     .attr('class', 'layer-mapillary-signs layer-mapillary-detections')
70652                     .style('display', enabled ? 'block' : 'none')
70653                     .merge(layer);
70654
70655                 if (enabled) {
70656                     if (service && ~~context.map().zoom() >= minZoom) {
70657                         editOn();
70658                         update();
70659                         service.loadSigns(projection);
70660                     } else {
70661                         editOff();
70662                     }
70663                 }
70664             }
70665
70666
70667             drawSigns.enabled = function(_) {
70668                 if (!arguments.length) { return svgMapillarySigns.enabled; }
70669                 svgMapillarySigns.enabled = _;
70670                 if (svgMapillarySigns.enabled) {
70671                     showLayer();
70672                 } else {
70673                     hideLayer();
70674                 }
70675                 dispatch.call('change');
70676                 return this;
70677             };
70678
70679
70680             drawSigns.supported = function() {
70681                 return !!getService();
70682             };
70683
70684
70685             init();
70686             return drawSigns;
70687         }
70688
70689         function svgMapillaryMapFeatures(projection, context, dispatch) {
70690             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70691             var minZoom = 12;
70692             var layer = select(null);
70693             var _mapillary;
70694
70695
70696             function init() {
70697                 if (svgMapillaryMapFeatures.initialized) { return; }  // run once
70698                 svgMapillaryMapFeatures.enabled = false;
70699                 svgMapillaryMapFeatures.initialized = true;
70700             }
70701
70702
70703             function getService() {
70704                 if (services.mapillary && !_mapillary) {
70705                     _mapillary = services.mapillary;
70706                     _mapillary.event.on('loadedMapFeatures', throttledRedraw);
70707                 } else if (!services.mapillary && _mapillary) {
70708                     _mapillary = null;
70709                 }
70710                 return _mapillary;
70711             }
70712
70713
70714             function showLayer() {
70715                 var service = getService();
70716                 if (!service) { return; }
70717
70718                 editOn();
70719             }
70720
70721
70722             function hideLayer() {
70723                 throttledRedraw.cancel();
70724                 editOff();
70725             }
70726
70727
70728             function editOn() {
70729                 layer.style('display', 'block');
70730             }
70731
70732
70733             function editOff() {
70734                 layer.selectAll('.icon-map-feature').remove();
70735                 layer.style('display', 'none');
70736             }
70737
70738
70739             function click(d) {
70740                 var service = getService();
70741                 if (!service) { return; }
70742
70743                 context.map().centerEase(d.loc);
70744
70745                 var selectedImageKey = service.getSelectedImageKey();
70746                 var imageKey;
70747
70748                 // Pick one of the images the map feature was detected in,
70749                 // preference given to an image already selected.
70750                 d.detections.forEach(function(detection) {
70751                     if (!imageKey || selectedImageKey === detection.image_key) {
70752                         imageKey = detection.image_key;
70753                     }
70754                 });
70755
70756                 service
70757                     .selectImage(context, imageKey)
70758                     .updateViewer(context, imageKey)
70759                     .showViewer(context);
70760             }
70761
70762
70763             function update() {
70764                 var service = getService();
70765                 var data = (service ? service.mapFeatures(projection) : []);
70766                 var selectedImageKey = service && service.getSelectedImageKey();
70767                 var transform = svgPointTransform(projection);
70768
70769                 var mapFeatures = layer.selectAll('.icon-map-feature')
70770                     .data(data, function(d) { return d.key; });
70771
70772                 // exit
70773                 mapFeatures.exit()
70774                     .remove();
70775
70776                 // enter
70777                 var enter = mapFeatures.enter()
70778                     .append('g')
70779                     .attr('class', 'icon-map-feature icon-detected')
70780                     .on('click', click);
70781
70782                 enter
70783                     .append('title')
70784                     .text(function(d) {
70785                         var id = d.value.replace(/--/g, '.').replace(/-/g, '_');
70786                         return _t('mapillary_map_features.' + id);
70787                     });
70788
70789                 enter
70790                     .append('use')
70791                     .attr('width', '24px')
70792                     .attr('height', '24px')
70793                     .attr('x', '-12px')
70794                     .attr('y', '-12px')
70795                     .attr('xlink:href', function(d) {
70796                         if (d.value === 'object--billboard') {
70797                             // no billboard icon right now, so use the advertisement icon
70798                             return '#object--sign--advertisement';
70799                         }
70800                         return '#' + d.value;
70801                     });
70802
70803                 enter
70804                     .append('rect')
70805                     .attr('width', '24px')
70806                     .attr('height', '24px')
70807                     .attr('x', '-12px')
70808                     .attr('y', '-12px');
70809
70810                 // update
70811                 mapFeatures
70812                     .merge(enter)
70813                     .attr('transform', transform)
70814                     .classed('currentView', function(d) {
70815                         return d.detections.some(function(detection) {
70816                             return detection.image_key === selectedImageKey;
70817                         });
70818                     })
70819                     .sort(function(a, b) {
70820                         var aSelected = a.detections.some(function(detection) {
70821                             return detection.image_key === selectedImageKey;
70822                         });
70823                         var bSelected = b.detections.some(function(detection) {
70824                             return detection.image_key === selectedImageKey;
70825                         });
70826                         if (aSelected === bSelected) {
70827                             return b.loc[1] - a.loc[1]; // sort Y
70828                         } else if (aSelected) {
70829                             return 1;
70830                         }
70831                         return -1;
70832                     });
70833             }
70834
70835
70836             function drawMapFeatures(selection) {
70837                 var enabled = svgMapillaryMapFeatures.enabled;
70838                 var service = getService();
70839
70840                 layer = selection.selectAll('.layer-mapillary-map-features')
70841                     .data(service ? [0] : []);
70842
70843                 layer.exit()
70844                     .remove();
70845
70846                 layer = layer.enter()
70847                     .append('g')
70848                     .attr('class', 'layer-mapillary-map-features layer-mapillary-detections')
70849                     .style('display', enabled ? 'block' : 'none')
70850                     .merge(layer);
70851
70852                 if (enabled) {
70853                     if (service && ~~context.map().zoom() >= minZoom) {
70854                         editOn();
70855                         update();
70856                         service.loadMapFeatures(projection);
70857                     } else {
70858                         editOff();
70859                     }
70860                 }
70861             }
70862
70863
70864             drawMapFeatures.enabled = function(_) {
70865                 if (!arguments.length) { return svgMapillaryMapFeatures.enabled; }
70866                 svgMapillaryMapFeatures.enabled = _;
70867                 if (svgMapillaryMapFeatures.enabled) {
70868                     showLayer();
70869                 } else {
70870                     hideLayer();
70871                 }
70872                 dispatch.call('change');
70873                 return this;
70874             };
70875
70876
70877             drawMapFeatures.supported = function() {
70878                 return !!getService();
70879             };
70880
70881
70882             init();
70883             return drawMapFeatures;
70884         }
70885
70886         function svgOpenstreetcamImages(projection, context, dispatch) {
70887             var throttledRedraw = throttle(function () { dispatch.call('change'); }, 1000);
70888             var minZoom = 12;
70889             var minMarkerZoom = 16;
70890             var minViewfieldZoom = 18;
70891             var layer = select(null);
70892             var _openstreetcam;
70893
70894
70895             function init() {
70896                 if (svgOpenstreetcamImages.initialized) { return; }  // run once
70897                 svgOpenstreetcamImages.enabled = false;
70898                 svgOpenstreetcamImages.initialized = true;
70899             }
70900
70901
70902             function getService() {
70903                 if (services.openstreetcam && !_openstreetcam) {
70904                     _openstreetcam = services.openstreetcam;
70905                     _openstreetcam.event.on('loadedImages', throttledRedraw);
70906                 } else if (!services.openstreetcam && _openstreetcam) {
70907                     _openstreetcam = null;
70908                 }
70909
70910                 return _openstreetcam;
70911             }
70912
70913
70914             function showLayer() {
70915                 var service = getService();
70916                 if (!service) { return; }
70917
70918                 editOn();
70919
70920                 layer
70921                     .style('opacity', 0)
70922                     .transition()
70923                     .duration(250)
70924                     .style('opacity', 1)
70925                     .on('end', function () { dispatch.call('change'); });
70926             }
70927
70928
70929             function hideLayer() {
70930                 throttledRedraw.cancel();
70931
70932                 layer
70933                     .transition()
70934                     .duration(250)
70935                     .style('opacity', 0)
70936                     .on('end', editOff);
70937             }
70938
70939
70940             function editOn() {
70941                 layer.style('display', 'block');
70942             }
70943
70944
70945             function editOff() {
70946                 layer.selectAll('.viewfield-group').remove();
70947                 layer.style('display', 'none');
70948             }
70949
70950
70951             function click(d) {
70952                 var service = getService();
70953                 if (!service) { return; }
70954
70955                 service
70956                     .selectImage(context, d)
70957                     .updateViewer(context, d)
70958                     .showViewer(context);
70959
70960                 context.map().centerEase(d.loc);
70961             }
70962
70963
70964             function mouseover(d) {
70965                 var service = getService();
70966                 if (service) { service.setStyles(context, d); }
70967             }
70968
70969
70970             function mouseout() {
70971                 var service = getService();
70972                 if (service) { service.setStyles(context, null); }
70973             }
70974
70975
70976             function transform(d) {
70977                 var t = svgPointTransform(projection)(d);
70978                 if (d.ca) {
70979                     t += ' rotate(' + Math.floor(d.ca) + ',0,0)';
70980                 }
70981                 return t;
70982             }
70983
70984
70985             context.photos().on('change.openstreetcam_images', update);
70986
70987             function update() {
70988                 var viewer = context.container().select('.photoviewer');
70989                 var selected = viewer.empty() ? undefined : viewer.datum();
70990
70991                 var z = ~~context.map().zoom();
70992                 var showMarkers = (z >= minMarkerZoom);
70993                 var showViewfields = (z >= minViewfieldZoom);
70994
70995                 var service = getService();
70996                 var sequences = [];
70997                 var images = [];
70998
70999                 if (context.photos().showsFlat()) {
71000                     sequences = (service ? service.sequences(projection) : []);
71001                     images = (service && showMarkers ? service.images(projection) : []);
71002                 }
71003
71004                 var traces = layer.selectAll('.sequences').selectAll('.sequence')
71005                     .data(sequences, function(d) { return d.properties.key; });
71006
71007                 // exit
71008                 traces.exit()
71009                     .remove();
71010
71011                 // enter/update
71012                 traces = traces.enter()
71013                     .append('path')
71014                     .attr('class', 'sequence')
71015                     .merge(traces)
71016                     .attr('d', svgPath(projection).geojson);
71017
71018
71019                 var groups = layer.selectAll('.markers').selectAll('.viewfield-group')
71020                     .data(images, function(d) { return d.key; });
71021
71022                 // exit
71023                 groups.exit()
71024                     .remove();
71025
71026                 // enter
71027                 var groupsEnter = groups.enter()
71028                     .append('g')
71029                     .attr('class', 'viewfield-group')
71030                     .on('mouseenter', mouseover)
71031                     .on('mouseleave', mouseout)
71032                     .on('click', click);
71033
71034                 groupsEnter
71035                     .append('g')
71036                     .attr('class', 'viewfield-scale');
71037
71038                 // update
71039                 var markers = groups
71040                     .merge(groupsEnter)
71041                     .sort(function(a, b) {
71042                         return (a === selected) ? 1
71043                             : (b === selected) ? -1
71044                             : b.loc[1] - a.loc[1];  // sort Y
71045                     })
71046                     .attr('transform', transform)
71047                     .select('.viewfield-scale');
71048
71049
71050                 markers.selectAll('circle')
71051                     .data([0])
71052                     .enter()
71053                     .append('circle')
71054                     .attr('dx', '0')
71055                     .attr('dy', '0')
71056                     .attr('r', '6');
71057
71058                 var viewfields = markers.selectAll('.viewfield')
71059                     .data(showViewfields ? [0] : []);
71060
71061                 viewfields.exit()
71062                     .remove();
71063
71064                 viewfields.enter()               // viewfields may or may not be drawn...
71065                     .insert('path', 'circle')    // but if they are, draw below the circles
71066                     .attr('class', 'viewfield')
71067                     .attr('transform', 'scale(1.5,1.5),translate(-8, -13)')
71068                     .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');
71069             }
71070
71071
71072             function drawImages(selection) {
71073                 var enabled = svgOpenstreetcamImages.enabled,
71074                     service = getService();
71075
71076                 layer = selection.selectAll('.layer-openstreetcam')
71077                     .data(service ? [0] : []);
71078
71079                 layer.exit()
71080                     .remove();
71081
71082                 var layerEnter = layer.enter()
71083                     .append('g')
71084                     .attr('class', 'layer-openstreetcam')
71085                     .style('display', enabled ? 'block' : 'none');
71086
71087                 layerEnter
71088                     .append('g')
71089                     .attr('class', 'sequences');
71090
71091                 layerEnter
71092                     .append('g')
71093                     .attr('class', 'markers');
71094
71095                 layer = layerEnter
71096                     .merge(layer);
71097
71098                 if (enabled) {
71099                     if (service && ~~context.map().zoom() >= minZoom) {
71100                         editOn();
71101                         update();
71102                         service.loadImages(projection);
71103                     } else {
71104                         editOff();
71105                     }
71106                 }
71107             }
71108
71109
71110             drawImages.enabled = function(_) {
71111                 if (!arguments.length) { return svgOpenstreetcamImages.enabled; }
71112                 svgOpenstreetcamImages.enabled = _;
71113                 if (svgOpenstreetcamImages.enabled) {
71114                     showLayer();
71115                 } else {
71116                     hideLayer();
71117                 }
71118                 dispatch.call('change');
71119                 return this;
71120             };
71121
71122
71123             drawImages.supported = function() {
71124                 return !!getService();
71125             };
71126
71127
71128             init();
71129             return drawImages;
71130         }
71131
71132         function svgOsm(projection, context, dispatch) {
71133             var enabled = true;
71134
71135
71136             function drawOsm(selection) {
71137                 selection.selectAll('.layer-osm')
71138                     .data(['covered', 'areas', 'lines', 'points', 'labels'])
71139                     .enter()
71140                     .append('g')
71141                     .attr('class', function(d) { return 'layer-osm ' + d; });
71142
71143                 selection.selectAll('.layer-osm.points').selectAll('.points-group')
71144                     .data(['points', 'midpoints', 'vertices', 'turns'])
71145                     .enter()
71146                     .append('g')
71147                     .attr('class', function(d) { return 'points-group ' + d; });
71148             }
71149
71150
71151             function showLayer() {
71152                 var layer = context.surface().selectAll('.data-layer.osm');
71153                 layer.interrupt();
71154
71155                 layer
71156                     .classed('disabled', false)
71157                     .style('opacity', 0)
71158                     .transition()
71159                     .duration(250)
71160                     .style('opacity', 1)
71161                     .on('end interrupt', function () {
71162                         dispatch.call('change');
71163                     });
71164             }
71165
71166
71167             function hideLayer() {
71168                 var layer = context.surface().selectAll('.data-layer.osm');
71169                 layer.interrupt();
71170
71171                 layer
71172                     .transition()
71173                     .duration(250)
71174                     .style('opacity', 0)
71175                     .on('end interrupt', function () {
71176                         layer.classed('disabled', true);
71177                         dispatch.call('change');
71178                     });
71179             }
71180
71181
71182             drawOsm.enabled = function(val) {
71183                 if (!arguments.length) { return enabled; }
71184                 enabled = val;
71185
71186                 if (enabled) {
71187                     showLayer();
71188                 } else {
71189                     hideLayer();
71190                 }
71191
71192                 dispatch.call('change');
71193                 return this;
71194             };
71195
71196
71197             return drawOsm;
71198         }
71199
71200         var _notesEnabled = false;
71201         var _osmService;
71202
71203
71204         function svgNotes(projection, context, dispatch$1) {
71205             if (!dispatch$1) { dispatch$1 = dispatch('change'); }
71206             var throttledRedraw = throttle(function () { dispatch$1.call('change'); }, 1000);
71207             var minZoom = 12;
71208             var touchLayer = select(null);
71209             var drawLayer = select(null);
71210             var _notesVisible = false;
71211
71212
71213             function markerPath(selection, klass) {
71214                 selection
71215                     .attr('class', klass)
71216                     .attr('transform', 'translate(-8, -22)')
71217                     .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');
71218             }
71219
71220
71221             // Loosely-coupled osm service for fetching notes.
71222             function getService() {
71223                 if (services.osm && !_osmService) {
71224                     _osmService = services.osm;
71225                     _osmService.on('loadedNotes', throttledRedraw);
71226                 } else if (!services.osm && _osmService) {
71227                     _osmService = null;
71228                 }
71229
71230                 return _osmService;
71231             }
71232
71233
71234             // Show the notes
71235             function editOn() {
71236                 if (!_notesVisible) {
71237                     _notesVisible = true;
71238                     drawLayer
71239                         .style('display', 'block');
71240                 }
71241             }
71242
71243
71244             // Immediately remove the notes and their touch targets
71245             function editOff() {
71246                 if (_notesVisible) {
71247                     _notesVisible = false;
71248                     drawLayer
71249                         .style('display', 'none');
71250                     drawLayer.selectAll('.note')
71251                         .remove();
71252                     touchLayer.selectAll('.note')
71253                         .remove();
71254                 }
71255             }
71256
71257
71258             // Enable the layer.  This shows the notes and transitions them to visible.
71259             function layerOn() {
71260                 editOn();
71261
71262                 drawLayer
71263                     .style('opacity', 0)
71264                     .transition()
71265                     .duration(250)
71266                     .style('opacity', 1)
71267                     .on('end interrupt', function () {
71268                         dispatch$1.call('change');
71269                     });
71270             }
71271
71272
71273             // Disable the layer.  This transitions the layer invisible and then hides the notes.
71274             function layerOff() {
71275                 throttledRedraw.cancel();
71276                 drawLayer.interrupt();
71277                 touchLayer.selectAll('.note')
71278                     .remove();
71279
71280                 drawLayer
71281                     .transition()
71282                     .duration(250)
71283                     .style('opacity', 0)
71284                     .on('end interrupt', function () {
71285                         editOff();
71286                         dispatch$1.call('change');
71287                     });
71288             }
71289
71290
71291             // Update the note markers
71292             function updateMarkers() {
71293                 if (!_notesVisible || !_notesEnabled) { return; }
71294
71295                 var service = getService();
71296                 var selectedID = context.selectedNoteID();
71297                 var data = (service ? service.notes(projection) : []);
71298                 var getTransform = svgPointTransform(projection);
71299
71300                 // Draw markers..
71301                 var notes = drawLayer.selectAll('.note')
71302                     .data(data, function(d) { return d.status + d.id; });
71303
71304                 // exit
71305                 notes.exit()
71306                     .remove();
71307
71308                 // enter
71309                 var notesEnter = notes.enter()
71310                     .append('g')
71311                     .attr('class', function(d) { return 'note note-' + d.id + ' ' + d.status; })
71312                     .classed('new', function(d) { return d.id < 0; });
71313
71314                 notesEnter
71315                     .append('ellipse')
71316                     .attr('cx', 0.5)
71317                     .attr('cy', 1)
71318                     .attr('rx', 6.5)
71319                     .attr('ry', 3)
71320                     .attr('class', 'stroke');
71321
71322                 notesEnter
71323                     .append('path')
71324                     .call(markerPath, 'shadow');
71325
71326                 notesEnter
71327                     .append('use')
71328                     .attr('class', 'note-fill')
71329                     .attr('width', '20px')
71330                     .attr('height', '20px')
71331                     .attr('x', '-8px')
71332                     .attr('y', '-22px')
71333                     .attr('xlink:href', '#iD-icon-note');
71334
71335                 notesEnter.selectAll('.icon-annotation')
71336                     .data(function(d) { return [d]; })
71337                     .enter()
71338                     .append('use')
71339                     .attr('class', 'icon-annotation')
71340                     .attr('width', '10px')
71341                     .attr('height', '10px')
71342                     .attr('x', '-3px')
71343                     .attr('y', '-19px')
71344                     .attr('xlink:href', function(d) {
71345                         return '#iD-icon-' + (d.id < 0 ? 'plus' : (d.status === 'open' ? 'close' : 'apply'));
71346                     });
71347
71348                 // update
71349                 notes
71350                     .merge(notesEnter)
71351                     .sort(sortY)
71352                     .classed('selected', function(d) {
71353                         var mode = context.mode();
71354                         var isMoving = mode && mode.id === 'drag-note';  // no shadows when dragging
71355                         return !isMoving && d.id === selectedID;
71356                     })
71357                     .attr('transform', getTransform);
71358
71359
71360                 // Draw targets..
71361                 if (touchLayer.empty()) { return; }
71362                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71363
71364                 var targets = touchLayer.selectAll('.note')
71365                     .data(data, function(d) { return d.id; });
71366
71367                 // exit
71368                 targets.exit()
71369                     .remove();
71370
71371                 // enter/update
71372                 targets.enter()
71373                     .append('rect')
71374                     .attr('width', '20px')
71375                     .attr('height', '20px')
71376                     .attr('x', '-8px')
71377                     .attr('y', '-22px')
71378                     .merge(targets)
71379                     .sort(sortY)
71380                     .attr('class', function(d) {
71381                         var newClass = (d.id < 0 ? 'new' : '');
71382                         return 'note target note-' + d.id + ' ' + fillClass + newClass;
71383                     })
71384                     .attr('transform', getTransform);
71385
71386
71387                 function sortY(a, b) {
71388                     return (a.id === selectedID) ? 1 : (b.id === selectedID) ? -1 : b.loc[1] - a.loc[1];
71389                 }
71390             }
71391
71392
71393             // Draw the notes layer and schedule loading notes and updating markers.
71394             function drawNotes(selection) {
71395                 var service = getService();
71396
71397                 var surface = context.surface();
71398                 if (surface && !surface.empty()) {
71399                     touchLayer = surface.selectAll('.data-layer.touch .layer-touch.markers');
71400                 }
71401
71402                 drawLayer = selection.selectAll('.layer-notes')
71403                     .data(service ? [0] : []);
71404
71405                 drawLayer.exit()
71406                     .remove();
71407
71408                 drawLayer = drawLayer.enter()
71409                     .append('g')
71410                     .attr('class', 'layer-notes')
71411                     .style('display', _notesEnabled ? 'block' : 'none')
71412                     .merge(drawLayer);
71413
71414                 if (_notesEnabled) {
71415                     if (service && ~~context.map().zoom() >= minZoom) {
71416                         editOn();
71417                         service.loadNotes(projection);
71418                         updateMarkers();
71419                     } else {
71420                         editOff();
71421                     }
71422                 }
71423             }
71424
71425
71426             // Toggles the layer on and off
71427             drawNotes.enabled = function(val) {
71428                 if (!arguments.length) { return _notesEnabled; }
71429
71430                 _notesEnabled = val;
71431                 if (_notesEnabled) {
71432                     layerOn();
71433                 } else {
71434                     layerOff();
71435                     if (context.selectedNoteID()) {
71436                         context.enter(modeBrowse(context));
71437                     }
71438                 }
71439
71440                 dispatch$1.call('change');
71441                 return this;
71442             };
71443
71444
71445             return drawNotes;
71446         }
71447
71448         function svgTouch() {
71449
71450             function drawTouch(selection) {
71451                 selection.selectAll('.layer-touch')
71452                     .data(['areas', 'lines', 'points', 'turns', 'markers'])
71453                     .enter()
71454                     .append('g')
71455                     .attr('class', function(d) { return 'layer-touch ' + d; });
71456             }
71457
71458             return drawTouch;
71459         }
71460
71461         function refresh(selection, node) {
71462             var cr = node.getBoundingClientRect();
71463             var prop = [cr.width, cr.height];
71464             selection.property('__dimensions__', prop);
71465             return prop;
71466         }
71467
71468         function utilGetDimensions(selection, force) {
71469             if (!selection || selection.empty()) {
71470                 return [0, 0];
71471             }
71472             var node = selection.node(),
71473                 cached = selection.property('__dimensions__');
71474             return (!cached || force) ? refresh(selection, node) : cached;
71475         }
71476
71477
71478         function utilSetDimensions(selection, dimensions) {
71479             if (!selection || selection.empty()) {
71480                 return selection;
71481             }
71482             var node = selection.node();
71483             if (dimensions === null) {
71484                 refresh(selection, node);
71485                 return selection;
71486             }
71487             return selection
71488                 .property('__dimensions__', [dimensions[0], dimensions[1]])
71489                 .attr('width', dimensions[0])
71490                 .attr('height', dimensions[1]);
71491         }
71492
71493         function svgLayers(projection, context) {
71494             var dispatch$1 = dispatch('change');
71495             var svg = select(null);
71496             var _layers = [
71497                 { id: 'osm', layer: svgOsm(projection, context, dispatch$1) },
71498                 { id: 'notes', layer: svgNotes(projection, context, dispatch$1) },
71499                 { id: 'data', layer: svgData(projection, context, dispatch$1) },
71500                 { id: 'keepRight', layer: svgKeepRight(projection, context, dispatch$1) },
71501                 { id: 'improveOSM', layer: svgImproveOSM(projection, context, dispatch$1) },
71502                 { id: 'osmose', layer: svgOsmose(projection, context, dispatch$1) },
71503                 { id: 'streetside', layer: svgStreetside(projection, context, dispatch$1)},
71504                 { id: 'mapillary', layer: svgMapillaryImages(projection, context, dispatch$1) },
71505                 { id: 'mapillary-map-features',  layer: svgMapillaryMapFeatures(projection, context, dispatch$1) },
71506                 { id: 'mapillary-signs',  layer: svgMapillarySigns(projection, context, dispatch$1) },
71507                 { id: 'openstreetcam', layer: svgOpenstreetcamImages(projection, context, dispatch$1) },
71508                 { id: 'debug', layer: svgDebug(projection, context) },
71509                 { id: 'geolocate', layer: svgGeolocate(projection) },
71510                 { id: 'touch', layer: svgTouch() }
71511             ];
71512
71513
71514             function drawLayers(selection) {
71515                 svg = selection.selectAll('.surface')
71516                     .data([0]);
71517
71518                 svg = svg.enter()
71519                     .append('svg')
71520                     .attr('class', 'surface')
71521                     .merge(svg);
71522
71523                 var defs = svg.selectAll('.surface-defs')
71524                     .data([0]);
71525
71526                 defs.enter()
71527                     .append('defs')
71528                     .attr('class', 'surface-defs');
71529
71530                 var groups = svg.selectAll('.data-layer')
71531                     .data(_layers);
71532
71533                 groups.exit()
71534                     .remove();
71535
71536                 groups.enter()
71537                     .append('g')
71538                     .attr('class', function(d) { return 'data-layer ' + d.id; })
71539                     .merge(groups)
71540                     .each(function(d) { select(this).call(d.layer); });
71541             }
71542
71543
71544             drawLayers.all = function() {
71545                 return _layers;
71546             };
71547
71548
71549             drawLayers.layer = function(id) {
71550                 var obj = _layers.find(function(o) { return o.id === id; });
71551                 return obj && obj.layer;
71552             };
71553
71554
71555             drawLayers.only = function(what) {
71556                 var arr = [].concat(what);
71557                 var all = _layers.map(function(layer) { return layer.id; });
71558                 return drawLayers.remove(utilArrayDifference(all, arr));
71559             };
71560
71561
71562             drawLayers.remove = function(what) {
71563                 var arr = [].concat(what);
71564                 arr.forEach(function(id) {
71565                     _layers = _layers.filter(function(o) { return o.id !== id; });
71566                 });
71567                 dispatch$1.call('change');
71568                 return this;
71569             };
71570
71571
71572             drawLayers.add = function(what) {
71573                 var arr = [].concat(what);
71574                 arr.forEach(function(obj) {
71575                     if ('id' in obj && 'layer' in obj) {
71576                         _layers.push(obj);
71577                     }
71578                 });
71579                 dispatch$1.call('change');
71580                 return this;
71581             };
71582
71583
71584             drawLayers.dimensions = function(val) {
71585                 if (!arguments.length) { return utilGetDimensions(svg); }
71586                 utilSetDimensions(svg, val);
71587                 return this;
71588             };
71589
71590
71591             return utilRebind(drawLayers, dispatch$1, 'on');
71592         }
71593
71594         function svgLines(projection, context) {
71595             var detected = utilDetect();
71596
71597             var highway_stack = {
71598                 motorway: 0,
71599                 motorway_link: 1,
71600                 trunk: 2,
71601                 trunk_link: 3,
71602                 primary: 4,
71603                 primary_link: 5,
71604                 secondary: 6,
71605                 tertiary: 7,
71606                 unclassified: 8,
71607                 residential: 9,
71608                 service: 10,
71609                 footway: 11
71610             };
71611
71612
71613             function drawTargets(selection, graph, entities, filter) {
71614                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71615                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
71616                 var getPath = svgPath(projection).geojson;
71617                 var activeID = context.activeID();
71618                 var base = context.history().base();
71619
71620                 // The targets and nopes will be MultiLineString sub-segments of the ways
71621                 var data = { targets: [], nopes: [] };
71622
71623                 entities.forEach(function(way) {
71624                     var features = svgSegmentWay(way, graph, activeID);
71625                     data.targets.push.apply(data.targets, features.passive);
71626                     data.nopes.push.apply(data.nopes, features.active);
71627                 });
71628
71629
71630                 // Targets allow hover and vertex snapping
71631                 var targetData = data.targets.filter(getPath);
71632                 var targets = selection.selectAll('.line.target-allowed')
71633                     .filter(function(d) { return filter(d.properties.entity); })
71634                     .data(targetData, function key(d) { return d.id; });
71635
71636                 // exit
71637                 targets.exit()
71638                     .remove();
71639
71640                 var segmentWasEdited = function(d) {
71641                     var wayID = d.properties.entity.id;
71642                     // if the whole line was edited, don't draw segment changes
71643                     if (!base.entities[wayID] ||
71644                         !fastDeepEqual(graph.entities[wayID].nodes, base.entities[wayID].nodes)) {
71645                         return false;
71646                     }
71647                     return d.properties.nodes.some(function(n) {
71648                         return !base.entities[n.id] ||
71649                                !fastDeepEqual(graph.entities[n.id].loc, base.entities[n.id].loc);
71650                     });
71651                 };
71652
71653                 // enter/update
71654                 targets.enter()
71655                     .append('path')
71656                     .merge(targets)
71657                     .attr('d', getPath)
71658                     .attr('class', function(d) {
71659                         return 'way line target target-allowed ' + targetClass + d.id;
71660                     })
71661                     .classed('segment-edited', segmentWasEdited);
71662
71663                 // NOPE
71664                 var nopeData = data.nopes.filter(getPath);
71665                 var nopes = selection.selectAll('.line.target-nope')
71666                     .filter(function(d) { return filter(d.properties.entity); })
71667                     .data(nopeData, function key(d) { return d.id; });
71668
71669                 // exit
71670                 nopes.exit()
71671                     .remove();
71672
71673                 // enter/update
71674                 nopes.enter()
71675                     .append('path')
71676                     .merge(nopes)
71677                     .attr('d', getPath)
71678                     .attr('class', function(d) {
71679                         return 'way line target target-nope ' + nopeClass + d.id;
71680                     })
71681                     .classed('segment-edited', segmentWasEdited);
71682             }
71683
71684
71685             function drawLines(selection, graph, entities, filter) {
71686                 var base = context.history().base();
71687
71688                 function waystack(a, b) {
71689                     var selected = context.selectedIDs();
71690                     var scoreA = selected.indexOf(a.id) !== -1 ? 20 : 0;
71691                     var scoreB = selected.indexOf(b.id) !== -1 ? 20 : 0;
71692
71693                     if (a.tags.highway) { scoreA -= highway_stack[a.tags.highway]; }
71694                     if (b.tags.highway) { scoreB -= highway_stack[b.tags.highway]; }
71695                     return scoreA - scoreB;
71696                 }
71697
71698
71699                 function drawLineGroup(selection, klass, isSelected) {
71700                     // Note: Don't add `.selected` class in draw modes
71701                     var mode = context.mode();
71702                     var isDrawing = mode && /^draw/.test(mode.id);
71703                     var selectedClass = (!isDrawing && isSelected) ? 'selected ' : '';
71704
71705                     var lines = selection
71706                         .selectAll('path')
71707                         .filter(filter)
71708                         .data(getPathData(isSelected), osmEntity.key);
71709
71710                     lines.exit()
71711                         .remove();
71712
71713                     // Optimization: Call expensive TagClasses only on enter selection. This
71714                     // works because osmEntity.key is defined to include the entity v attribute.
71715                     lines.enter()
71716                         .append('path')
71717                         .attr('class', function(d) {
71718
71719                             var prefix = 'way line';
71720
71721                             // if this line isn't styled by its own tags
71722                             if (!d.hasInterestingTags()) {
71723
71724                                 var parentRelations = graph.parentRelations(d);
71725                                 var parentMultipolygons = parentRelations.filter(function(relation) {
71726                                     return relation.isMultipolygon();
71727                                 });
71728
71729                                 // and if it's a member of at least one multipolygon relation
71730                                 if (parentMultipolygons.length > 0 &&
71731                                     // and only multipolygon relations
71732                                     parentRelations.length === parentMultipolygons.length) {
71733                                     // then fudge the classes to style this as an area edge
71734                                     prefix = 'relation area';
71735                                 }
71736                             }
71737
71738                             var oldMPClass = oldMultiPolygonOuters[d.id] ? 'old-multipolygon ' : '';
71739                             return prefix + ' ' + klass + ' ' + selectedClass + oldMPClass + d.id;
71740                         })
71741                         .classed('added', function(d) {
71742                             return !base.entities[d.id];
71743                         })
71744                         .classed('geometry-edited', function(d) {
71745                             return graph.entities[d.id] &&
71746                                 base.entities[d.id] &&
71747                                 !fastDeepEqual(graph.entities[d.id].nodes, base.entities[d.id].nodes);
71748                         })
71749                         .classed('retagged', function(d) {
71750                             return graph.entities[d.id] &&
71751                                 base.entities[d.id] &&
71752                                 !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
71753                         })
71754                         .call(svgTagClasses())
71755                         .merge(lines)
71756                         .sort(waystack)
71757                         .attr('d', getPath)
71758                         .call(svgTagClasses().tags(svgRelationMemberTags(graph)));
71759
71760                     return selection;
71761                 }
71762
71763
71764                 function getPathData(isSelected) {
71765                     return function() {
71766                         var layer = this.parentNode.__data__;
71767                         var data = pathdata[layer] || [];
71768                         return data.filter(function(d) {
71769                             if (isSelected)
71770                                 { return context.selectedIDs().indexOf(d.id) !== -1; }
71771                             else
71772                                 { return context.selectedIDs().indexOf(d.id) === -1; }
71773                         });
71774                     };
71775                 }
71776
71777                 function addMarkers(layergroup, pathclass, groupclass, groupdata, marker) {
71778                     var markergroup = layergroup
71779                         .selectAll('g.' + groupclass)
71780                         .data([pathclass]);
71781
71782                     markergroup = markergroup.enter()
71783                         .append('g')
71784                         .attr('class', groupclass)
71785                         .merge(markergroup);
71786
71787                     var markers = markergroup
71788                         .selectAll('path')
71789                         .filter(filter)
71790                         .data(
71791                             function data() { return groupdata[this.parentNode.__data__] || []; },
71792                             function key(d) { return [d.id, d.index]; }
71793                         );
71794
71795                     markers.exit()
71796                         .remove();
71797
71798                     markers = markers.enter()
71799                         .append('path')
71800                         .attr('class', pathclass)
71801                         .merge(markers)
71802                         .attr('marker-mid', marker)
71803                         .attr('d', function(d) { return d.d; });
71804
71805                     if (detected.ie) {
71806                         markers.each(function() { this.parentNode.insertBefore(this, this); });
71807                     }
71808                 }
71809
71810
71811                 var getPath = svgPath(projection, graph);
71812                 var ways = [];
71813                 var onewaydata = {};
71814                 var sideddata = {};
71815                 var oldMultiPolygonOuters = {};
71816
71817                 for (var i = 0; i < entities.length; i++) {
71818                     var entity = entities[i];
71819                     var outer = osmOldMultipolygonOuterMember(entity, graph);
71820                     if (outer) {
71821                         ways.push(entity.mergeTags(outer.tags));
71822                         oldMultiPolygonOuters[outer.id] = true;
71823                     } else if (entity.geometry(graph) === 'line') {
71824                         ways.push(entity);
71825                     }
71826                 }
71827
71828                 ways = ways.filter(getPath);
71829                 var pathdata = utilArrayGroupBy(ways, function(way) { return way.layer(); });
71830
71831                 Object.keys(pathdata).forEach(function(k) {
71832                     var v = pathdata[k];
71833                     var onewayArr = v.filter(function(d) { return d.isOneWay(); });
71834                     var onewaySegments = svgMarkerSegments(
71835                         projection, graph, 35,
71836                         function shouldReverse(entity) { return entity.tags.oneway === '-1'; },
71837                         function bothDirections(entity) {
71838                             return entity.tags.oneway === 'reversible' || entity.tags.oneway === 'alternating';
71839                         }
71840                     );
71841                     onewaydata[k] = utilArrayFlatten(onewayArr.map(onewaySegments));
71842
71843                     var sidedArr = v.filter(function(d) { return d.isSided(); });
71844                     var sidedSegments = svgMarkerSegments(
71845                         projection, graph, 30,
71846                         function shouldReverse() { return false; },
71847                         function bothDirections() { return false; }
71848                     );
71849                     sideddata[k] = utilArrayFlatten(sidedArr.map(sidedSegments));
71850                 });
71851
71852
71853                 var covered = selection.selectAll('.layer-osm.covered');     // under areas
71854                 var uncovered = selection.selectAll('.layer-osm.lines');     // over areas
71855                 var touchLayer = selection.selectAll('.layer-touch.lines');
71856
71857                 // Draw lines..
71858                 [covered, uncovered].forEach(function(selection) {
71859                     var range = (selection === covered ? range$1(-10,0) : range$1(0,11));
71860                     var layergroup = selection
71861                         .selectAll('g.layergroup')
71862                         .data(range);
71863
71864                     layergroup = layergroup.enter()
71865                         .append('g')
71866                         .attr('class', function(d) { return 'layergroup layer' + String(d); })
71867                         .merge(layergroup);
71868
71869                     layergroup
71870                         .selectAll('g.linegroup')
71871                         .data(['shadow', 'casing', 'stroke', 'shadow-highlighted', 'casing-highlighted', 'stroke-highlighted'])
71872                         .enter()
71873                         .append('g')
71874                         .attr('class', function(d) { return 'linegroup line-' + d; });
71875
71876                     layergroup.selectAll('g.line-shadow')
71877                         .call(drawLineGroup, 'shadow', false);
71878                     layergroup.selectAll('g.line-casing')
71879                         .call(drawLineGroup, 'casing', false);
71880                     layergroup.selectAll('g.line-stroke')
71881                         .call(drawLineGroup, 'stroke', false);
71882
71883                     layergroup.selectAll('g.line-shadow-highlighted')
71884                         .call(drawLineGroup, 'shadow', true);
71885                     layergroup.selectAll('g.line-casing-highlighted')
71886                         .call(drawLineGroup, 'casing', true);
71887                     layergroup.selectAll('g.line-stroke-highlighted')
71888                         .call(drawLineGroup, 'stroke', true);
71889
71890                     addMarkers(layergroup, 'oneway', 'onewaygroup', onewaydata, 'url(#ideditor-oneway-marker)');
71891                     addMarkers(layergroup, 'sided', 'sidedgroup', sideddata,
71892                         function marker(d) {
71893                             var category = graph.entity(d.id).sidednessIdentifier();
71894                             return 'url(#ideditor-sided-marker-' + category + ')';
71895                         }
71896                     );
71897                 });
71898
71899                 // Draw touch targets..
71900                 touchLayer
71901                     .call(drawTargets, graph, ways, filter);
71902             }
71903
71904
71905             return drawLines;
71906         }
71907
71908         function svgMidpoints(projection, context) {
71909             var targetRadius = 8;
71910
71911             function drawTargets(selection, graph, entities, filter) {
71912                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
71913                 var getTransform = svgPointTransform(projection).geojson;
71914
71915                 var data = entities.map(function(midpoint) {
71916                     return {
71917                         type: 'Feature',
71918                         id: midpoint.id,
71919                         properties: {
71920                             target: true,
71921                             entity: midpoint
71922                         },
71923                         geometry: {
71924                             type: 'Point',
71925                             coordinates: midpoint.loc
71926                         }
71927                     };
71928                 });
71929
71930                 var targets = selection.selectAll('.midpoint.target')
71931                     .filter(function(d) { return filter(d.properties.entity); })
71932                     .data(data, function key(d) { return d.id; });
71933
71934                 // exit
71935                 targets.exit()
71936                     .remove();
71937
71938                 // enter/update
71939                 targets.enter()
71940                     .append('circle')
71941                     .attr('r', targetRadius)
71942                     .merge(targets)
71943                     .attr('class', function(d) { return 'node midpoint target ' + fillClass + d.id; })
71944                     .attr('transform', getTransform);
71945             }
71946
71947
71948             function drawMidpoints(selection, graph, entities, filter, extent) {
71949                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.midpoints');
71950                 var touchLayer = selection.selectAll('.layer-touch.points');
71951
71952                 var mode = context.mode();
71953                 if ((mode && mode.id !== 'select') || !context.map().withinEditableZoom()) {
71954                     drawLayer.selectAll('.midpoint').remove();
71955                     touchLayer.selectAll('.midpoint.target').remove();
71956                     return;
71957                 }
71958
71959                 var poly = extent.polygon();
71960                 var midpoints = {};
71961
71962                 for (var i = 0; i < entities.length; i++) {
71963                     var entity = entities[i];
71964
71965                     if (entity.type !== 'way') { continue; }
71966                     if (!filter(entity)) { continue; }
71967                     if (context.selectedIDs().indexOf(entity.id) < 0) { continue; }
71968
71969                     var nodes = graph.childNodes(entity);
71970                     for (var j = 0; j < nodes.length - 1; j++) {
71971                         var a = nodes[j];
71972                         var b = nodes[j + 1];
71973                         var id = [a.id, b.id].sort().join('-');
71974
71975                         if (midpoints[id]) {
71976                             midpoints[id].parents.push(entity);
71977                         } else if (geoVecLength(projection(a.loc), projection(b.loc)) > 40) {
71978                             var point = geoVecInterp(a.loc, b.loc, 0.5);
71979                             var loc = null;
71980
71981                             if (extent.intersects(point)) {
71982                                 loc = point;
71983                             } else {
71984                                 for (var k = 0; k < 4; k++) {
71985                                     point = geoLineIntersection([a.loc, b.loc], [poly[k], poly[k + 1]]);
71986                                     if (point &&
71987                                         geoVecLength(projection(a.loc), projection(point)) > 20 &&
71988                                         geoVecLength(projection(b.loc), projection(point)) > 20)
71989                                     {
71990                                         loc = point;
71991                                         break;
71992                                     }
71993                                 }
71994                             }
71995
71996                             if (loc) {
71997                                 midpoints[id] = {
71998                                     type: 'midpoint',
71999                                     id: id,
72000                                     loc: loc,
72001                                     edge: [a.id, b.id],
72002                                     parents: [entity]
72003                                 };
72004                             }
72005                         }
72006                     }
72007                 }
72008
72009
72010                 function midpointFilter(d) {
72011                     if (midpoints[d.id])
72012                         { return true; }
72013
72014                     for (var i = 0; i < d.parents.length; i++) {
72015                         if (filter(d.parents[i])) {
72016                             return true;
72017                         }
72018                     }
72019
72020                     return false;
72021                 }
72022
72023
72024                 var groups = drawLayer.selectAll('.midpoint')
72025                     .filter(midpointFilter)
72026                     .data(Object.values(midpoints), function(d) { return d.id; });
72027
72028                 groups.exit()
72029                     .remove();
72030
72031                 var enter = groups.enter()
72032                     .insert('g', ':first-child')
72033                     .attr('class', 'midpoint');
72034
72035                 enter
72036                     .append('polygon')
72037                     .attr('points', '-6,8 10,0 -6,-8')
72038                     .attr('class', 'shadow');
72039
72040                 enter
72041                     .append('polygon')
72042                     .attr('points', '-3,4 5,0 -3,-4')
72043                     .attr('class', 'fill');
72044
72045                 groups = groups
72046                     .merge(enter)
72047                     .attr('transform', function(d) {
72048                         var translate = svgPointTransform(projection);
72049                         var a = graph.entity(d.edge[0]);
72050                         var b = graph.entity(d.edge[1]);
72051                         var angle = geoAngle(a, b, projection) * (180 / Math.PI);
72052                         return translate(d) + ' rotate(' + angle + ')';
72053                     })
72054                     .call(svgTagClasses().tags(
72055                         function(d) { return d.parents[0].tags; }
72056                     ));
72057
72058                 // Propagate data bindings.
72059                 groups.select('polygon.shadow');
72060                 groups.select('polygon.fill');
72061
72062
72063                 // Draw touch targets..
72064                 touchLayer
72065                     .call(drawTargets, graph, Object.values(midpoints), midpointFilter);
72066             }
72067
72068             return drawMidpoints;
72069         }
72070
72071         function svgPoints(projection, context) {
72072
72073             function markerPath(selection, klass) {
72074                 selection
72075                     .attr('class', klass)
72076                     .attr('transform', 'translate(-8, -23)')
72077                     .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');
72078             }
72079
72080             function sortY(a, b) {
72081                 return b.loc[1] - a.loc[1];
72082             }
72083
72084
72085             // Avoid exit/enter if we're just moving stuff around.
72086             // The node will get a new version but we only need to run the update selection.
72087             function fastEntityKey(d) {
72088                 var mode = context.mode();
72089                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72090                 return isMoving ? d.id : osmEntity.key(d);
72091             }
72092
72093
72094             function drawTargets(selection, graph, entities, filter) {
72095                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72096                 var getTransform = svgPointTransform(projection).geojson;
72097                 var activeID = context.activeID();
72098                 var data = [];
72099
72100                 entities.forEach(function(node) {
72101                     if (activeID === node.id) { return; }   // draw no target on the activeID
72102
72103                     data.push({
72104                         type: 'Feature',
72105                         id: node.id,
72106                         properties: {
72107                             target: true,
72108                             entity: node
72109                         },
72110                         geometry: node.asGeoJSON()
72111                     });
72112                 });
72113
72114                 var targets = selection.selectAll('.point.target')
72115                     .filter(function(d) { return filter(d.properties.entity); })
72116                     .data(data, function key(d) { return d.id; });
72117
72118                 // exit
72119                 targets.exit()
72120                     .remove();
72121
72122                 // enter/update
72123                 targets.enter()
72124                     .append('rect')
72125                     .attr('x', -10)
72126                     .attr('y', -26)
72127                     .attr('width', 20)
72128                     .attr('height', 30)
72129                     .merge(targets)
72130                     .attr('class', function(d) { return 'node point target ' + fillClass + d.id; })
72131                     .attr('transform', getTransform);
72132             }
72133
72134
72135             function drawPoints(selection, graph, entities, filter) {
72136                 var wireframe = context.surface().classed('fill-wireframe');
72137                 var zoom = geoScaleToZoom(projection.scale());
72138                 var base = context.history().base();
72139
72140                 // Points with a direction will render as vertices at higher zooms..
72141                 function renderAsPoint(entity) {
72142                     return entity.geometry(graph) === 'point' &&
72143                         !(zoom >= 18 && entity.directions(graph, projection).length);
72144                 }
72145
72146                 // All points will render as vertices in wireframe mode too..
72147                 var points = wireframe ? [] : entities.filter(renderAsPoint);
72148                 points.sort(sortY);
72149
72150
72151                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.points');
72152                 var touchLayer = selection.selectAll('.layer-touch.points');
72153
72154                 // Draw points..
72155                 var groups = drawLayer.selectAll('g.point')
72156                     .filter(filter)
72157                     .data(points, fastEntityKey);
72158
72159                 groups.exit()
72160                     .remove();
72161
72162                 var enter = groups.enter()
72163                     .append('g')
72164                     .attr('class', function(d) { return 'node point ' + d.id; })
72165                     .order();
72166
72167                 enter
72168                     .append('path')
72169                     .call(markerPath, 'shadow');
72170
72171                 enter
72172                     .append('ellipse')
72173                     .attr('cx', 0.5)
72174                     .attr('cy', 1)
72175                     .attr('rx', 6.5)
72176                     .attr('ry', 3)
72177                     .attr('class', 'stroke');
72178
72179                 enter
72180                     .append('path')
72181                     .call(markerPath, 'stroke');
72182
72183                 enter
72184                     .append('use')
72185                     .attr('transform', 'translate(-5, -19)')
72186                     .attr('class', 'icon')
72187                     .attr('width', '11px')
72188                     .attr('height', '11px');
72189
72190                 groups = groups
72191                     .merge(enter)
72192                     .attr('transform', svgPointTransform(projection))
72193                     .classed('added', function(d) {
72194                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72195                     })
72196                     .classed('moved', function(d) {
72197                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72198                     })
72199                     .classed('retagged', function(d) {
72200                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72201                     })
72202                     .call(svgTagClasses());
72203
72204                 groups.select('.shadow');   // propagate bound data
72205                 groups.select('.stroke');   // propagate bound data
72206                 groups.select('.icon')      // propagate bound data
72207                     .attr('xlink:href', function(entity) {
72208                         var preset = _mainPresetIndex.match(entity, graph);
72209                         var picon = preset && preset.icon;
72210
72211                         if (!picon) {
72212                             return '';
72213                         } else {
72214                             var isMaki = /^maki-/.test(picon);
72215                             return '#' + picon + (isMaki ? '-11' : '');
72216                         }
72217                     });
72218
72219
72220                 // Draw touch targets..
72221                 touchLayer
72222                     .call(drawTargets, graph, points, filter);
72223             }
72224
72225
72226             return drawPoints;
72227         }
72228
72229         function svgTurns(projection, context) {
72230
72231             function icon(turn) {
72232                 var u = turn.u ? '-u' : '';
72233                 if (turn.no) { return '#iD-turn-no' + u; }
72234                 if (turn.only) { return '#iD-turn-only' + u; }
72235                 return '#iD-turn-yes' + u;
72236             }
72237
72238             function drawTurns(selection, graph, turns) {
72239
72240                 function turnTransform(d) {
72241                     var pxRadius = 50;
72242                     var toWay = graph.entity(d.to.way);
72243                     var toPoints = graph.childNodes(toWay)
72244                         .map(function (n) { return n.loc; })
72245                         .map(projection);
72246                     var toLength = geoPathLength(toPoints);
72247                     var mid = toLength / 2;    // midpoint of destination way
72248
72249                     var toNode = graph.entity(d.to.node);
72250                     var toVertex = graph.entity(d.to.vertex);
72251                     var a = geoAngle(toVertex, toNode, projection);
72252                     var o = projection(toVertex.loc);
72253                     var r = d.u ? 0                  // u-turn: no radius
72254                         : !toWay.__via ? pxRadius    // leaf way: put marker at pxRadius
72255                         : Math.min(mid, pxRadius);   // via way: prefer pxRadius, fallback to mid for very short ways
72256
72257                     return 'translate(' + (r * Math.cos(a) + o[0]) + ',' + (r * Math.sin(a) + o[1]) + ') ' +
72258                         'rotate(' + a * 180 / Math.PI + ')';
72259                 }
72260
72261
72262                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.turns');
72263                 var touchLayer = selection.selectAll('.layer-touch.turns');
72264
72265                 // Draw turns..
72266                 var groups = drawLayer.selectAll('g.turn')
72267                     .data(turns, function(d) { return d.key; });
72268
72269                 // exit
72270                 groups.exit()
72271                     .remove();
72272
72273                 // enter
72274                 var groupsEnter = groups.enter()
72275                     .append('g')
72276                     .attr('class', function(d) { return 'turn ' + d.key; });
72277
72278                 var turnsEnter = groupsEnter
72279                     .filter(function(d) { return !d.u; });
72280
72281                 turnsEnter.append('rect')
72282                     .attr('transform', 'translate(-22, -12)')
72283                     .attr('width', '44')
72284                     .attr('height', '24');
72285
72286                 turnsEnter.append('use')
72287                     .attr('transform', 'translate(-22, -12)')
72288                     .attr('width', '44')
72289                     .attr('height', '24');
72290
72291                 var uEnter = groupsEnter
72292                     .filter(function(d) { return d.u; });
72293
72294                 uEnter.append('circle')
72295                     .attr('r', '16');
72296
72297                 uEnter.append('use')
72298                     .attr('transform', 'translate(-16, -16)')
72299                     .attr('width', '32')
72300                     .attr('height', '32');
72301
72302                 // update
72303                 groups = groups
72304                     .merge(groupsEnter)
72305                     .attr('opacity', function(d) { return d.direct === false ? '0.7' : null; })
72306                     .attr('transform', turnTransform);
72307
72308                 groups.select('use')
72309                     .attr('xlink:href', icon);
72310
72311                 groups.select('rect');      // propagate bound data
72312                 groups.select('circle');    // propagate bound data
72313
72314
72315                 // Draw touch targets..
72316                 var fillClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72317                 groups = touchLayer.selectAll('g.turn')
72318                     .data(turns, function(d) { return d.key; });
72319
72320                 // exit
72321                 groups.exit()
72322                     .remove();
72323
72324                 // enter
72325                 groupsEnter = groups.enter()
72326                     .append('g')
72327                     .attr('class', function(d) { return 'turn ' + d.key; });
72328
72329                 turnsEnter = groupsEnter
72330                     .filter(function(d) { return !d.u; });
72331
72332                 turnsEnter.append('rect')
72333                     .attr('class', 'target ' + fillClass)
72334                     .attr('transform', 'translate(-22, -12)')
72335                     .attr('width', '44')
72336                     .attr('height', '24');
72337
72338                 uEnter = groupsEnter
72339                     .filter(function(d) { return d.u; });
72340
72341                 uEnter.append('circle')
72342                     .attr('class', 'target ' + fillClass)
72343                     .attr('r', '16');
72344
72345                 // update
72346                 groups = groups
72347                     .merge(groupsEnter)
72348                     .attr('transform', turnTransform);
72349
72350                 groups.select('rect');      // propagate bound data
72351                 groups.select('circle');    // propagate bound data
72352
72353
72354                 return this;
72355             }
72356
72357             return drawTurns;
72358         }
72359
72360         function svgVertices(projection, context) {
72361             var radiuses = {
72362                 //       z16-, z17,   z18+,  w/icon
72363                 shadow: [6,    7.5,   7.5,   12],
72364                 stroke: [2.5,  3.5,   3.5,   8],
72365                 fill:   [1,    1.5,   1.5,   1.5]
72366             };
72367
72368             var _currHoverTarget;
72369             var _currPersistent = {};
72370             var _currHover = {};
72371             var _prevHover = {};
72372             var _currSelected = {};
72373             var _prevSelected = {};
72374             var _radii = {};
72375
72376
72377             function sortY(a, b) {
72378                 return b.loc[1] - a.loc[1];
72379             }
72380
72381             // Avoid exit/enter if we're just moving stuff around.
72382             // The node will get a new version but we only need to run the update selection.
72383             function fastEntityKey(d) {
72384                 var mode = context.mode();
72385                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72386                 return isMoving ? d.id : osmEntity.key(d);
72387             }
72388
72389
72390             function draw(selection, graph, vertices, sets, filter) {
72391                 sets = sets || { selected: {}, important: {}, hovered: {} };
72392
72393                 var icons = {};
72394                 var directions = {};
72395                 var wireframe = context.surface().classed('fill-wireframe');
72396                 var zoom = geoScaleToZoom(projection.scale());
72397                 var z = (zoom < 17 ? 0 : zoom < 18 ? 1 : 2);
72398                 var activeID = context.activeID();
72399                 var base = context.history().base();
72400
72401
72402                 function getIcon(d) {
72403                     // always check latest entity, as fastEntityKey avoids enter/exit now
72404                     var entity = graph.entity(d.id);
72405                     if (entity.id in icons) { return icons[entity.id]; }
72406
72407                     icons[entity.id] =
72408                         entity.hasInterestingTags() &&
72409                         _mainPresetIndex.match(entity, graph).icon;
72410
72411                     return icons[entity.id];
72412                 }
72413
72414
72415                 // memoize directions results, return false for empty arrays (for use in filter)
72416                 function getDirections(entity) {
72417                     if (entity.id in directions) { return directions[entity.id]; }
72418
72419                     var angles = entity.directions(graph, projection);
72420                     directions[entity.id] = angles.length ? angles : false;
72421                     return angles;
72422                 }
72423
72424
72425                 function updateAttributes(selection) {
72426                     ['shadow', 'stroke', 'fill'].forEach(function(klass) {
72427                         var rads = radiuses[klass];
72428                         selection.selectAll('.' + klass)
72429                             .each(function(entity) {
72430                                 var i = z && getIcon(entity);
72431                                 var r = rads[i ? 3 : z];
72432
72433                                 // slightly increase the size of unconnected endpoints #3775
72434                                 if (entity.id !== activeID && entity.isEndpoint(graph) && !entity.isConnected(graph)) {
72435                                     r += 1.5;
72436                                 }
72437
72438                                 if (klass === 'shadow') {   // remember this value, so we don't need to
72439                                     _radii[entity.id] = r;  // recompute it when we draw the touch targets
72440                                 }
72441
72442                                 select(this)
72443                                     .attr('r', r)
72444                                     .attr('visibility', (i && klass === 'fill') ? 'hidden' : null);
72445                             });
72446                     });
72447                 }
72448
72449                 vertices.sort(sortY);
72450
72451                 var groups = selection.selectAll('g.vertex')
72452                     .filter(filter)
72453                     .data(vertices, fastEntityKey);
72454
72455                 // exit
72456                 groups.exit()
72457                     .remove();
72458
72459                 // enter
72460                 var enter = groups.enter()
72461                     .append('g')
72462                     .attr('class', function(d) { return 'node vertex ' + d.id; })
72463                     .order();
72464
72465                 enter
72466                     .append('circle')
72467                     .attr('class', 'shadow');
72468
72469                 enter
72470                     .append('circle')
72471                     .attr('class', 'stroke');
72472
72473                 // Vertices with tags get a fill.
72474                 enter.filter(function(d) { return d.hasInterestingTags(); })
72475                     .append('circle')
72476                     .attr('class', 'fill');
72477
72478                 // update
72479                 groups = groups
72480                     .merge(enter)
72481                     .attr('transform', svgPointTransform(projection))
72482                     .classed('sibling', function(d) { return d.id in sets.selected; })
72483                     .classed('shared', function(d) { return graph.isShared(d); })
72484                     .classed('endpoint', function(d) { return d.isEndpoint(graph); })
72485                     .classed('added', function(d) {
72486                         return !base.entities[d.id]; // if it doesn't exist in the base graph, it's new
72487                     })
72488                     .classed('moved', function(d) {
72489                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].loc, base.entities[d.id].loc);
72490                     })
72491                     .classed('retagged', function(d) {
72492                         return base.entities[d.id] && !fastDeepEqual(graph.entities[d.id].tags, base.entities[d.id].tags);
72493                     })
72494                     .call(updateAttributes);
72495
72496                 // Vertices with icons get a `use`.
72497                 var iconUse = groups
72498                     .selectAll('.icon')
72499                     .data(function data(d) { return zoom >= 17 && getIcon(d) ? [d] : []; }, fastEntityKey);
72500
72501                 // exit
72502                 iconUse.exit()
72503                     .remove();
72504
72505                 // enter
72506                 iconUse.enter()
72507                     .append('use')
72508                     .attr('class', 'icon')
72509                     .attr('width', '11px')
72510                     .attr('height', '11px')
72511                     .attr('transform', 'translate(-5.5, -5.5)')
72512                     .attr('xlink:href', function(d) {
72513                         var picon = getIcon(d);
72514                         var isMaki = /^maki-/.test(picon);
72515                         return '#' + picon + (isMaki ? '-11' : '');
72516                     });
72517
72518
72519                 // Vertices with directions get viewfields
72520                 var dgroups = groups
72521                     .selectAll('.viewfieldgroup')
72522                     .data(function data(d) { return zoom >= 18 && getDirections(d) ? [d] : []; }, fastEntityKey);
72523
72524                 // exit
72525                 dgroups.exit()
72526                     .remove();
72527
72528                 // enter/update
72529                 dgroups = dgroups.enter()
72530                     .insert('g', '.shadow')
72531                     .attr('class', 'viewfieldgroup')
72532                     .merge(dgroups);
72533
72534                 var viewfields = dgroups.selectAll('.viewfield')
72535                     .data(getDirections, function key(d) { return osmEntity.key(d); });
72536
72537                 // exit
72538                 viewfields.exit()
72539                     .remove();
72540
72541                 // enter/update
72542                 viewfields.enter()
72543                     .append('path')
72544                     .attr('class', 'viewfield')
72545                     .attr('d', 'M0,0H0')
72546                     .merge(viewfields)
72547                     .attr('marker-start', 'url(#ideditor-viewfield-marker' + (wireframe ? '-wireframe' : '') + ')')
72548                     .attr('transform', function(d) { return 'rotate(' + d + ')'; });
72549             }
72550
72551
72552             function drawTargets(selection, graph, entities, filter) {
72553                 var targetClass = context.getDebug('target') ? 'pink ' : 'nocolor ';
72554                 var nopeClass = context.getDebug('target') ? 'red ' : 'nocolor ';
72555                 var getTransform = svgPointTransform(projection).geojson;
72556                 var activeID = context.activeID();
72557                 var data = { targets: [], nopes: [] };
72558
72559                 entities.forEach(function(node) {
72560                     if (activeID === node.id) { return; }   // draw no target on the activeID
72561
72562                     var vertexType = svgPassiveVertex(node, graph, activeID);
72563                     if (vertexType !== 0) {     // passive or adjacent - allow to connect
72564                         data.targets.push({
72565                             type: 'Feature',
72566                             id: node.id,
72567                             properties: {
72568                                 target: true,
72569                                 entity: node
72570                             },
72571                             geometry: node.asGeoJSON()
72572                         });
72573                     } else {
72574                         data.nopes.push({
72575                             type: 'Feature',
72576                             id: node.id + '-nope',
72577                             properties: {
72578                                 nope: true,
72579                                 target: true,
72580                                 entity: node
72581                             },
72582                             geometry: node.asGeoJSON()
72583                         });
72584                     }
72585                 });
72586
72587                 // Targets allow hover and vertex snapping
72588                 var targets = selection.selectAll('.vertex.target-allowed')
72589                     .filter(function(d) { return filter(d.properties.entity); })
72590                     .data(data.targets, function key(d) { return d.id; });
72591
72592                 // exit
72593                 targets.exit()
72594                     .remove();
72595
72596                 // enter/update
72597                 targets.enter()
72598                     .append('circle')
72599                     .attr('r', function(d) {
72600                         return _radii[d.id]
72601                           || radiuses.shadow[3];
72602                     })
72603                     .merge(targets)
72604                     .attr('class', function(d) {
72605                         return 'node vertex target target-allowed '
72606                         + targetClass + d.id;
72607                     })
72608                     .attr('transform', getTransform);
72609
72610
72611                 // NOPE
72612                 var nopes = selection.selectAll('.vertex.target-nope')
72613                     .filter(function(d) { return filter(d.properties.entity); })
72614                     .data(data.nopes, function key(d) { return d.id; });
72615
72616                 // exit
72617                 nopes.exit()
72618                     .remove();
72619
72620                 // enter/update
72621                 nopes.enter()
72622                     .append('circle')
72623                     .attr('r', function(d) { return (_radii[d.properties.entity.id] || radiuses.shadow[3]); })
72624                     .merge(nopes)
72625                     .attr('class', function(d) { return 'node vertex target target-nope ' + nopeClass + d.id; })
72626                     .attr('transform', getTransform);
72627             }
72628
72629
72630             // Points can also render as vertices:
72631             // 1. in wireframe mode or
72632             // 2. at higher zooms if they have a direction
72633             function renderAsVertex(entity, graph, wireframe, zoom) {
72634                 var geometry = entity.geometry(graph);
72635                 return geometry === 'vertex' || (geometry === 'point' && (
72636                     wireframe || (zoom >= 18 && entity.directions(graph, projection).length)
72637                 ));
72638             }
72639
72640
72641             function isEditedNode(node, base, head) {
72642                 var baseNode = base.entities[node.id];
72643                 var headNode = head.entities[node.id];
72644                 return !headNode ||
72645                     !baseNode ||
72646                     !fastDeepEqual(headNode.tags, baseNode.tags) ||
72647                     !fastDeepEqual(headNode.loc, baseNode.loc);
72648             }
72649
72650
72651             function getSiblingAndChildVertices(ids, graph, wireframe, zoom) {
72652                 var results = {};
72653
72654                 var seenIds = {};
72655
72656                 function addChildVertices(entity) {
72657
72658                     // avoid redunant work and infinite recursion of circular relations
72659                     if (seenIds[entity.id]) { return; }
72660                     seenIds[entity.id] = true;
72661
72662                     var geometry = entity.geometry(graph);
72663                     if (!context.features().isHiddenFeature(entity, graph, geometry)) {
72664                         var i;
72665                         if (entity.type === 'way') {
72666                             for (i = 0; i < entity.nodes.length; i++) {
72667                                 var child = graph.hasEntity(entity.nodes[i]);
72668                                 if (child) {
72669                                     addChildVertices(child);
72670                                 }
72671                             }
72672                         } else if (entity.type === 'relation') {
72673                             for (i = 0; i < entity.members.length; i++) {
72674                                 var member = graph.hasEntity(entity.members[i].id);
72675                                 if (member) {
72676                                     addChildVertices(member);
72677                                 }
72678                             }
72679                         } else if (renderAsVertex(entity, graph, wireframe, zoom)) {
72680                             results[entity.id] = entity;
72681                         }
72682                     }
72683                 }
72684
72685                 ids.forEach(function(id) {
72686                     var entity = graph.hasEntity(id);
72687                     if (!entity) { return; }
72688
72689                     if (entity.type === 'node') {
72690                         if (renderAsVertex(entity, graph, wireframe, zoom)) {
72691                             results[entity.id] = entity;
72692                             graph.parentWays(entity).forEach(function(entity) {
72693                                 addChildVertices(entity);
72694                             });
72695                         }
72696                     } else {  // way, relation
72697                         addChildVertices(entity);
72698                     }
72699                 });
72700
72701                 return results;
72702             }
72703
72704
72705             function drawVertices(selection, graph, entities, filter, extent, fullRedraw) {
72706                 var wireframe = context.surface().classed('fill-wireframe');
72707                 var visualDiff = context.surface().classed('highlight-edited');
72708                 var zoom = geoScaleToZoom(projection.scale());
72709                 var mode = context.mode();
72710                 var isMoving = mode && /^(add|draw|drag|move|rotate)/.test(mode.id);
72711                 var base = context.history().base();
72712
72713                 var drawLayer = selection.selectAll('.layer-osm.points .points-group.vertices');
72714                 var touchLayer = selection.selectAll('.layer-touch.points');
72715
72716                 if (fullRedraw) {
72717                     _currPersistent = {};
72718                     _radii = {};
72719                 }
72720
72721                 // Collect important vertices from the `entities` list..
72722                 // (during a paritial redraw, it will not contain everything)
72723                 for (var i = 0; i < entities.length; i++) {
72724                     var entity = entities[i];
72725                     var geometry = entity.geometry(graph);
72726                     var keep = false;
72727
72728                     // a point that looks like a vertex..
72729                     if ((geometry === 'point') && renderAsVertex(entity, graph, wireframe, zoom)) {
72730                         _currPersistent[entity.id] = entity;
72731                         keep = true;
72732
72733                     // a vertex of some importance..
72734                     } else if (geometry === 'vertex' &&
72735                         (entity.hasInterestingTags() || entity.isEndpoint(graph) || entity.isConnected(graph)
72736                         || (visualDiff && isEditedNode(entity, base, graph)))) {
72737                         _currPersistent[entity.id] = entity;
72738                         keep = true;
72739                     }
72740
72741                     // whatever this is, it's not a persistent vertex..
72742                     if (!keep && !fullRedraw) {
72743                         delete _currPersistent[entity.id];
72744                     }
72745                 }
72746
72747                 // 3 sets of vertices to consider:
72748                 var sets = {
72749                     persistent: _currPersistent,  // persistent = important vertices (render always)
72750                     selected: _currSelected,      // selected + siblings of selected (render always)
72751                     hovered: _currHover           // hovered + siblings of hovered (render only in draw modes)
72752                 };
72753
72754                 var all = Object.assign({}, (isMoving ? _currHover : {}), _currSelected, _currPersistent);
72755
72756                 // Draw the vertices..
72757                 // The filter function controls the scope of what objects d3 will touch (exit/enter/update)
72758                 // Adjust the filter function to expand the scope beyond whatever entities were passed in.
72759                 var filterRendered = function(d) {
72760                     return d.id in _currPersistent || d.id in _currSelected || d.id in _currHover || filter(d);
72761                 };
72762                 drawLayer
72763                     .call(draw, graph, currentVisible(all), sets, filterRendered);
72764
72765                 // Draw touch targets..
72766                 // When drawing, render all targets (not just those affected by a partial redraw)
72767                 var filterTouch = function(d) {
72768                     return isMoving ? true : filterRendered(d);
72769                 };
72770                 touchLayer
72771                     .call(drawTargets, graph, currentVisible(all), filterTouch);
72772
72773
72774                 function currentVisible(which) {
72775                     return Object.keys(which)
72776                         .map(graph.hasEntity, graph)     // the current version of this entity
72777                         .filter(function (entity) { return entity && entity.intersects(extent, graph); });
72778                 }
72779             }
72780
72781
72782             // partial redraw - only update the selected items..
72783             drawVertices.drawSelected = function(selection, graph, extent) {
72784                 var wireframe = context.surface().classed('fill-wireframe');
72785                 var zoom = geoScaleToZoom(projection.scale());
72786
72787                 _prevSelected = _currSelected || {};
72788                 if (context.map().isInWideSelection()) {
72789                     _currSelected = {};
72790                     context.selectedIDs().forEach(function(id) {
72791                         var entity = graph.hasEntity(id);
72792                         if (!entity) { return; }
72793
72794                         if (entity.type === 'node') {
72795                             if (renderAsVertex(entity, graph, wireframe, zoom)) {
72796                                 _currSelected[entity.id] = entity;
72797                             }
72798                         }
72799                     });
72800
72801                 } else {
72802                     _currSelected = getSiblingAndChildVertices(context.selectedIDs(), graph, wireframe, zoom);
72803                 }
72804
72805                 // note that drawVertices will add `_currSelected` automatically if needed..
72806                 var filter = function(d) { return d.id in _prevSelected; };
72807                 drawVertices(selection, graph, Object.values(_prevSelected), filter, extent, false);
72808             };
72809
72810
72811             // partial redraw - only update the hovered items..
72812             drawVertices.drawHover = function(selection, graph, target, extent) {
72813                 if (target === _currHoverTarget) { return; }  // continue only if something changed
72814
72815                 var wireframe = context.surface().classed('fill-wireframe');
72816                 var zoom = geoScaleToZoom(projection.scale());
72817
72818                 _prevHover = _currHover || {};
72819                 _currHoverTarget = target;
72820                 var entity = target && target.properties && target.properties.entity;
72821
72822                 if (entity) {
72823                     _currHover = getSiblingAndChildVertices([entity.id], graph, wireframe, zoom);
72824                 } else {
72825                     _currHover = {};
72826                 }
72827
72828                 // note that drawVertices will add `_currHover` automatically if needed..
72829                 var filter = function(d) { return d.id in _prevHover; };
72830                 drawVertices(selection, graph, Object.values(_prevHover), filter, extent, false);
72831             };
72832
72833             return drawVertices;
72834         }
72835
72836         function utilBindOnce(target, type, listener, capture) {
72837             var typeOnce = type + '.once';
72838             function one() {
72839                 target.on(typeOnce, null);
72840                 listener.apply(this, arguments);
72841             }
72842             target.on(typeOnce, one, capture);
72843             return this;
72844         }
72845
72846         // Adapted from d3-zoom to handle pointer events.
72847
72848         // Ignore right-click, since that should open the context menu.
72849         function defaultFilter$2() {
72850           return !event.ctrlKey && !event.button;
72851         }
72852
72853         function defaultExtent$1() {
72854           var e = this;
72855           if (e instanceof SVGElement) {
72856             e = e.ownerSVGElement || e;
72857             if (e.hasAttribute('viewBox')) {
72858               e = e.viewBox.baseVal;
72859               return [[e.x, e.y], [e.x + e.width, e.y + e.height]];
72860             }
72861             return [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]];
72862           }
72863           return [[0, 0], [e.clientWidth, e.clientHeight]];
72864         }
72865
72866         function defaultWheelDelta$1() {
72867           return -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002);
72868         }
72869
72870         function defaultConstrain$1(transform, extent, translateExtent) {
72871           var dx0 = transform.invertX(extent[0][0]) - translateExtent[0][0],
72872               dx1 = transform.invertX(extent[1][0]) - translateExtent[1][0],
72873               dy0 = transform.invertY(extent[0][1]) - translateExtent[0][1],
72874               dy1 = transform.invertY(extent[1][1]) - translateExtent[1][1];
72875           return transform.translate(
72876             dx1 > dx0 ? (dx0 + dx1) / 2 : Math.min(0, dx0) || Math.max(0, dx1),
72877             dy1 > dy0 ? (dy0 + dy1) / 2 : Math.min(0, dy0) || Math.max(0, dy1)
72878           );
72879         }
72880
72881         function utilZoomPan() {
72882           var filter = defaultFilter$2,
72883               extent = defaultExtent$1,
72884               constrain = defaultConstrain$1,
72885               wheelDelta = defaultWheelDelta$1,
72886               scaleExtent = [0, Infinity],
72887               translateExtent = [[-Infinity, -Infinity], [Infinity, Infinity]],
72888               interpolate = interpolateZoom,
72889               listeners = dispatch('start', 'zoom', 'end'),
72890               _wheelDelay = 150,
72891               _transform = identity$2,
72892               _activeGesture;
72893
72894           function zoom(selection) {
72895             selection
72896                 .on('pointerdown.zoom', pointerdown)
72897                 .on('wheel.zoom', wheeled)
72898                 .style('touch-action', 'none')
72899                 .style('-webkit-tap-highlight-color', 'rgba(0,0,0,0)');
72900
72901             select(window)
72902                 .on('pointermove.zoompan', pointermove)
72903                 .on('pointerup.zoompan pointercancel.zoompan', pointerup);
72904           }
72905
72906           zoom.transform = function(collection, transform, point) {
72907             var selection = collection.selection ? collection.selection() : collection;
72908             if (collection !== selection) {
72909               schedule(collection, transform, point);
72910             } else {
72911               selection.interrupt().each(function() {
72912                 gesture(this, arguments)
72913                     .start()
72914                     .zoom(null, typeof transform === 'function' ? transform.apply(this, arguments) : transform)
72915                     .end();
72916               });
72917             }
72918           };
72919
72920           zoom.scaleBy = function(selection, k, p) {
72921             zoom.scaleTo(selection, function() {
72922               var k0 = _transform.k,
72923                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72924               return k0 * k1;
72925             }, p);
72926           };
72927
72928           zoom.scaleTo = function(selection, k, p) {
72929             zoom.transform(selection, function() {
72930               var e = extent.apply(this, arguments),
72931                   t0 = _transform,
72932                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p,
72933                   p1 = t0.invert(p0),
72934                   k1 = typeof k === 'function' ? k.apply(this, arguments) : k;
72935               return constrain(translate(scale(t0, k1), p0, p1), e, translateExtent);
72936             }, p);
72937           };
72938
72939           zoom.translateBy = function(selection, x, y) {
72940             zoom.transform(selection, function() {
72941               return constrain(_transform.translate(
72942                 typeof x === 'function' ? x.apply(this, arguments) : x,
72943                 typeof y === 'function' ? y.apply(this, arguments) : y
72944               ), extent.apply(this, arguments), translateExtent);
72945             });
72946           };
72947
72948           zoom.translateTo = function(selection, x, y, p) {
72949             zoom.transform(selection, function() {
72950               var e = extent.apply(this, arguments),
72951                   t = _transform,
72952                   p0 = p == null ? centroid(e) : typeof p === 'function' ? p.apply(this, arguments) : p;
72953               return constrain(identity$2.translate(p0[0], p0[1]).scale(t.k).translate(
72954                 typeof x === 'function' ? -x.apply(this, arguments) : -x,
72955                 typeof y === 'function' ? -y.apply(this, arguments) : -y
72956               ), e, translateExtent);
72957             }, p);
72958           };
72959
72960           function scale(transform, k) {
72961             k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], k));
72962             return k === transform.k ? transform : new Transform(k, transform.x, transform.y);
72963           }
72964
72965           function translate(transform, p0, p1) {
72966             var x = p0[0] - p1[0] * transform.k, y = p0[1] - p1[1] * transform.k;
72967             return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
72968           }
72969
72970           function centroid(extent) {
72971             return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
72972           }
72973
72974           function schedule(transition, transform, point) {
72975             transition
72976                 .on('start.zoom', function() { gesture(this, arguments).start(); })
72977                 .on('interrupt.zoom end.zoom', function() { gesture(this, arguments).end(); })
72978                 .tween('zoom', function() {
72979                   var that = this,
72980                       args = arguments,
72981                       g = gesture(that, args),
72982                       e = extent.apply(that, args),
72983                       p = point == null ? centroid(e) : typeof point === 'function' ? point.apply(that, args) : point,
72984                       w = Math.max(e[1][0] - e[0][0], e[1][1] - e[0][1]),
72985                       a = _transform,
72986                       b = typeof transform === 'function' ? transform.apply(that, args) : transform,
72987                       i = interpolate(a.invert(p).concat(w / a.k), b.invert(p).concat(w / b.k));
72988                   return function(t) {
72989                     if (t === 1) { t = b; } // Avoid rounding error on end.
72990                     else { var l = i(t), k = w / l[2]; t = new Transform(k, p[0] - l[0] * k, p[1] - l[1] * k); }
72991                     g.zoom(null, t);
72992                   };
72993                 });
72994           }
72995
72996           function gesture(that, args, clean) {
72997             return (!clean && _activeGesture) || new Gesture(that, args);
72998           }
72999
73000           function Gesture(that, args) {
73001             this.that = that;
73002             this.args = args;
73003             this.active = 0;
73004             this.extent = extent.apply(that, args);
73005           }
73006
73007           Gesture.prototype = {
73008             start: function() {
73009               if (++this.active === 1) {
73010                 _activeGesture = this;
73011                 this.emit('start');
73012               }
73013               return this;
73014             },
73015             zoom: function(key, transform) {
73016               if (this.mouse && key !== 'mouse') { this.mouse[1] = transform.invert(this.mouse[0]); }
73017               if (this.pointer0 && key !== 'touch') { this.pointer0[1] = transform.invert(this.pointer0[0]); }
73018               if (this.pointer1 && key !== 'touch') { this.pointer1[1] = transform.invert(this.pointer1[0]); }
73019               _transform = transform;
73020               this.emit('zoom');
73021               return this;
73022             },
73023             end: function() {
73024               if (--this.active === 0) {
73025                 _activeGesture = null;
73026                 this.emit('end');
73027               }
73028               return this;
73029             },
73030             emit: function(type) {
73031               customEvent(new ZoomEvent(zoom, type, _transform), listeners.apply, listeners, [type, this.that, this.args]);
73032             }
73033           };
73034
73035           function wheeled() {
73036             if (!filter.apply(this, arguments)) { return; }
73037             var g = gesture(this, arguments),
73038                 t = _transform,
73039                 k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], t.k * Math.pow(2, wheelDelta.apply(this, arguments)))),
73040                 p = utilFastMouse(this)(event);
73041
73042             // If the mouse is in the same location as before, reuse it.
73043             // If there were recent wheel events, reset the wheel idle timeout.
73044             if (g.wheel) {
73045               if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
73046                 g.mouse[1] = t.invert(g.mouse[0] = p);
73047               }
73048               clearTimeout(g.wheel);
73049
73050             // Otherwise, capture the mouse point and location at the start.
73051             } else {
73052               g.mouse = [p, t.invert(p)];
73053               interrupt(this);
73054               g.start();
73055             }
73056
73057             event.preventDefault();
73058             event.stopImmediatePropagation();
73059             g.wheel = setTimeout(wheelidled, _wheelDelay);
73060             g.zoom('mouse', constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
73061
73062             function wheelidled() {
73063               g.wheel = null;
73064               g.end();
73065             }
73066           }
73067
73068           var _downPointerIDs = new Set();
73069           var _pointerLocGetter;
73070
73071           function pointerdown() {
73072             _downPointerIDs.add(event.pointerId);
73073
73074             if (!filter.apply(this, arguments)) { return; }
73075
73076             var g = gesture(this, arguments, _downPointerIDs.size === 1);
73077             var started;
73078
73079             event.stopImmediatePropagation();
73080             _pointerLocGetter = utilFastMouse(this);
73081             var loc = _pointerLocGetter(event);
73082             var p = [loc, _transform.invert(loc), event.pointerId];
73083             if (!g.pointer0) {
73084                g.pointer0 = p;
73085                started = true;
73086
73087             } else if (!g.pointer1 && g.pointer0[2] !== p[2]) {
73088                g.pointer1 = p;
73089             }
73090
73091             if (started) {
73092               interrupt(this);
73093               g.start();
73094             }
73095           }
73096
73097           function pointermove() {
73098             if (!_downPointerIDs.has(event.pointerId)) { return; }
73099
73100             if (!_activeGesture || !_pointerLocGetter) { return; }
73101
73102             var g = gesture(this, arguments);
73103
73104             var isPointer0 = g.pointer0 && g.pointer0[2] === event.pointerId;
73105             var isPointer1 = !isPointer0 && g.pointer1 && g.pointer1[2] === event.pointerId;
73106
73107             if ((isPointer0 || isPointer1) && 'buttons' in event && !event.buttons) {
73108               // The pointer went up without ending the gesture somehow, e.g.
73109               // a down mouse was moved off the map and released. End it here.
73110               if (g.pointer0) { _downPointerIDs.delete(g.pointer0[2]); }
73111               if (g.pointer1) { _downPointerIDs.delete(g.pointer1[2]); }
73112               g.end();
73113               return;
73114             }
73115
73116             event.preventDefault();
73117             event.stopImmediatePropagation();
73118
73119             var loc = _pointerLocGetter(event);
73120             var t, p, l;
73121
73122             if (isPointer0) { g.pointer0[0] = loc; }
73123             else if (isPointer1) { g.pointer1[0] = loc; }
73124
73125             t = _transform;
73126             if (g.pointer1) {
73127               var p0 = g.pointer0[0], l0 = g.pointer0[1],
73128                   p1 = g.pointer1[0], l1 = g.pointer1[1],
73129                   dp = (dp = p1[0] - p0[0]) * dp + (dp = p1[1] - p0[1]) * dp,
73130                   dl = (dl = l1[0] - l0[0]) * dl + (dl = l1[1] - l0[1]) * dl;
73131               t = scale(t, Math.sqrt(dp / dl));
73132               p = [(p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2];
73133               l = [(l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2];
73134             } else if (g.pointer0) {
73135               p = g.pointer0[0];
73136               l = g.pointer0[1];
73137             }
73138             else { return; }
73139             g.zoom('touch', constrain(translate(t, p, l), g.extent, translateExtent));
73140           }
73141
73142           function pointerup() {
73143             if (!_downPointerIDs.has(event.pointerId)) { return; }
73144
73145             _downPointerIDs.delete(event.pointerId);
73146
73147             if (!_activeGesture) { return; }
73148
73149             var g = gesture(this, arguments);
73150
73151             event.stopImmediatePropagation();
73152
73153             if (g.pointer0 && g.pointer0[2] === event.pointerId) { delete g.pointer0; }
73154             else if (g.pointer1 && g.pointer1[2] === event.pointerId) { delete g.pointer1; }
73155
73156             if (g.pointer1 && !g.pointer0) {
73157               g.pointer0 = g.pointer1;
73158               delete g.pointer1;
73159             }
73160             if (g.pointer0) { g.pointer0[1] = _transform.invert(g.pointer0[0]); }
73161             else {
73162               g.end();
73163             }
73164           }
73165
73166           zoom.wheelDelta = function(_) {
73167             return arguments.length ? (wheelDelta = utilFunctor(+_), zoom) : wheelDelta;
73168           };
73169
73170           zoom.filter = function(_) {
73171             return arguments.length ? (filter = utilFunctor(!!_), zoom) : filter;
73172           };
73173
73174           zoom.extent = function(_) {
73175             return arguments.length ? (extent = utilFunctor([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
73176           };
73177
73178           zoom.scaleExtent = function(_) {
73179             return arguments.length ? (scaleExtent[0] = +_[0], scaleExtent[1] = +_[1], zoom) : [scaleExtent[0], scaleExtent[1]];
73180           };
73181
73182           zoom.translateExtent = function(_) {
73183             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]]];
73184           };
73185
73186           zoom.constrain = function(_) {
73187             return arguments.length ? (constrain = _, zoom) : constrain;
73188           };
73189
73190           zoom.interpolate = function(_) {
73191             return arguments.length ? (interpolate = _, zoom) : interpolate;
73192           };
73193
73194           zoom._transform = function(_) {
73195             return arguments.length ? (_transform = _, zoom) : _transform;
73196           };
73197
73198           zoom.on = function() {
73199             var value = listeners.on.apply(listeners, arguments);
73200             return value === listeners ? zoom : value;
73201           };
73202
73203           return zoom;
73204         }
73205
73206         // A custom double-click / double-tap event detector that works on touch devices
73207         // if pointer events are supported. Falls back to default `dblclick` event.
73208         function utilDoubleUp() {
73209
73210             var dispatch$1 = dispatch('doubleUp');
73211
73212             var _maxTimespan = 500; // milliseconds
73213             var _maxDistance = 20; // web pixels; be somewhat generous to account for touch devices
73214             var _pointer; // object representing the pointer that could trigger double up
73215
73216             function pointerIsValidFor(loc) {
73217                 // second pointerup must occur within a small timeframe after the first pointerdown
73218                 return new Date().getTime() - _pointer.startTime <= _maxTimespan &&
73219                     // all pointer events must occur within a small distance of the first pointerdown
73220                     geoVecLength(_pointer.startLoc, loc) <= _maxDistance;
73221             }
73222
73223             function pointerdown() {
73224
73225                 // ignore right-click
73226                 if (event.ctrlKey || event.button === 2) { return; }
73227
73228                 var loc = [event.clientX, event.clientY];
73229
73230                 // Don't rely on pointerId here since it can change between pointerdown
73231                 // events on touch devices
73232                 if (_pointer && !pointerIsValidFor(loc)) {
73233                     // if this pointer is no longer valid, clear it so another can be started
73234                     _pointer = undefined;
73235                 }
73236
73237                 if (!_pointer) {
73238                     _pointer = {
73239                         startLoc: loc,
73240                         startTime: new Date().getTime(),
73241                         upCount: 0,
73242                         pointerId: event.pointerId
73243                     };
73244                 } else { // double down
73245                     _pointer.pointerId = event.pointerId;
73246                 }
73247             }
73248
73249             function pointerup() {
73250
73251                 // ignore right-click
73252                 if (event.ctrlKey || event.button === 2) { return; }
73253
73254                 if (!_pointer || _pointer.pointerId !== event.pointerId) { return; }
73255
73256                 _pointer.upCount += 1;
73257
73258                 if (_pointer.upCount === 2) { // double up!
73259                     var loc = [event.clientX, event.clientY];
73260                     if (pointerIsValidFor(loc)) {
73261                         var locInThis = utilFastMouse(this)(event);
73262                         dispatch$1.call('doubleUp', this, locInThis);
73263                     }
73264                     // clear the pointer info in any case
73265                     _pointer = undefined;
73266                 }
73267             }
73268
73269             function doubleUp(selection) {
73270                 if ('PointerEvent' in window) {
73271                     // dblclick isn't well supported on touch devices so manually use
73272                     // pointer events if they're available
73273                     selection
73274                         .on('pointerdown.doubleUp', pointerdown)
73275                         .on('pointerup.doubleUp', pointerup);
73276                 } else {
73277                     // fallback to dblclick
73278                     selection
73279                         .on('dblclick.doubleUp', function() {
73280                             dispatch$1.call('doubleUp', this, utilFastMouse(this)(event));
73281                         });
73282                 }
73283             }
73284
73285             doubleUp.off = function(selection) {
73286                 selection
73287                     .on('pointerdown.doubleUp', null)
73288                     .on('pointerup.doubleUp', null)
73289                     .on('dblclick.doubleUp', null);
73290             };
73291
73292             return utilRebind(doubleUp, dispatch$1, 'on');
73293         }
73294
73295         // constants
73296         var TILESIZE = 256;
73297         var minZoom = 2;
73298         var maxZoom = 24;
73299         var kMin = geoZoomToScale(minZoom, TILESIZE);
73300         var kMax = geoZoomToScale(maxZoom, TILESIZE);
73301
73302         function clamp(num, min, max) {
73303             return Math.max(min, Math.min(num, max));
73304         }
73305
73306
73307         function rendererMap(context) {
73308             var dispatch$1 = dispatch(
73309                 'move', 'drawn',
73310                 'crossEditableZoom', 'hitMinZoom',
73311                 'changeHighlighting', 'changeAreaFill'
73312             );
73313             var projection = context.projection;
73314             var curtainProjection = context.curtainProjection;
73315             var drawLayers;
73316             var drawPoints;
73317             var drawVertices;
73318             var drawLines;
73319             var drawAreas;
73320             var drawMidpoints;
73321             var drawLabels;
73322
73323             var _selection = select(null);
73324             var supersurface = select(null);
73325             var wrapper = select(null);
73326             var surface = select(null);
73327
73328             var _dimensions = [1, 1];
73329             var _dblClickZoomEnabled = true;
73330             var _redrawEnabled = true;
73331             var _gestureTransformStart;
73332             var _transformStart = projection.transform();
73333             var _transformLast;
73334             var _isTransformed = false;
73335             var _minzoom = 0;
73336             var _getMouseCoords;
73337             var _lastPointerEvent;
73338             var _lastWithinEditableZoom;
73339
73340             // whether a pointerdown event started the zoom
73341             var _pointerDown = false;
73342
73343             // use pointer events on supported platforms; fallback to mouse events
73344             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
73345
73346             // use pointer event interaction if supported; fallback to touch/mouse events in d3-zoom
73347             var _zoomerPannerFunction = 'PointerEvent' in window ? utilZoomPan : d3_zoom;
73348
73349             var _zoomerPanner = _zoomerPannerFunction()
73350                 .scaleExtent([kMin, kMax])
73351                 .interpolate(interpolate)
73352                 .filter(zoomEventFilter)
73353                 .on('zoom.map', zoomPan)
73354                 .on('start.map', function() {
73355                     _pointerDown = event.sourceEvent && event.sourceEvent.type === 'pointerdown';
73356                 })
73357                 .on('end.map', function() {
73358                     _pointerDown = false;
73359                 });
73360             var _doubleUpHandler = utilDoubleUp();
73361
73362             var scheduleRedraw = throttle(redraw, 750);
73363             // var isRedrawScheduled = false;
73364             // var pendingRedrawCall;
73365             // function scheduleRedraw() {
73366             //     // Only schedule the redraw if one has not already been set.
73367             //     if (isRedrawScheduled) return;
73368             //     isRedrawScheduled = true;
73369             //     var that = this;
73370             //     var args = arguments;
73371             //     pendingRedrawCall = window.requestIdleCallback(function () {
73372             //         // Reset the boolean so future redraws can be set.
73373             //         isRedrawScheduled = false;
73374             //         redraw.apply(that, args);
73375             //     }, { timeout: 1400 });
73376             // }
73377
73378             function cancelPendingRedraw() {
73379                 scheduleRedraw.cancel();
73380                 // isRedrawScheduled = false;
73381                 // window.cancelIdleCallback(pendingRedrawCall);
73382             }
73383
73384
73385             function map(selection) {
73386                 _selection = selection;
73387
73388                 context
73389                     .on('change.map', immediateRedraw);
73390
73391                 var osm = context.connection();
73392                 if (osm) {
73393                     osm.on('change.map', immediateRedraw);
73394                 }
73395
73396                 function didUndoOrRedo(targetTransform) {
73397                     var mode = context.mode().id;
73398                     if (mode !== 'browse' && mode !== 'select') { return; }
73399                     if (targetTransform) {
73400                         map.transformEase(targetTransform);
73401                     }
73402                 }
73403
73404                 context.history()
73405                     .on('merge.map', function() { scheduleRedraw(); })
73406                     .on('change.map', immediateRedraw)
73407                     .on('undone.map', function(stack, fromStack) {
73408                         didUndoOrRedo(fromStack.transform);
73409                     })
73410                     .on('redone.map', function(stack) {
73411                         didUndoOrRedo(stack.transform);
73412                     });
73413
73414                 context.background()
73415                     .on('change.map', immediateRedraw);
73416
73417                 context.features()
73418                     .on('redraw.map', immediateRedraw);
73419
73420                 drawLayers
73421                     .on('change.map', function() {
73422                         context.background().updateImagery();
73423                         immediateRedraw();
73424                     });
73425
73426                 selection
73427                     .on('wheel.map mousewheel.map', function() {
73428                         // disable swipe-to-navigate browser pages on trackpad/magic mouse – #5552
73429                         event.preventDefault();
73430                     })
73431                     .call(_zoomerPanner)
73432                     .call(_zoomerPanner.transform, projection.transform())
73433                     .on('dblclick.zoom', null); // override d3-zoom dblclick handling
73434
73435                 map.supersurface = supersurface = selection.append('div')
73436                     .attr('class', 'supersurface')
73437                     .call(utilSetTransform, 0, 0);
73438
73439                 // Need a wrapper div because Opera can't cope with an absolutely positioned
73440                 // SVG element: http://bl.ocks.org/jfirebaugh/6fbfbd922552bf776c16
73441                 wrapper = supersurface
73442                     .append('div')
73443                     .attr('class', 'layer layer-data');
73444
73445                 map.surface = surface = wrapper
73446                     .call(drawLayers)
73447                     .selectAll('.surface');
73448
73449                 surface
73450                     .call(drawLabels.observe)
73451                     .call(_doubleUpHandler)
73452                     .on(_pointerPrefix + 'down.zoom', function() {
73453                         _lastPointerEvent = event;
73454                         if (event.button === 2) {
73455                             event.stopPropagation();
73456                         }
73457                     }, true)
73458                     .on(_pointerPrefix + 'up.zoom', function() {
73459                         _lastPointerEvent = event;
73460                         if (resetTransform()) {
73461                             immediateRedraw();
73462                         }
73463                     })
73464                     .on(_pointerPrefix + 'move.map', function() {
73465                         _lastPointerEvent = event;
73466                     })
73467                     .on(_pointerPrefix + 'over.vertices', function() {
73468                         if (map.editableDataEnabled() && !_isTransformed) {
73469                             var hover = event.target.__data__;
73470                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73471                             dispatch$1.call('drawn', this, { full: false });
73472                         }
73473                     })
73474                     .on(_pointerPrefix + 'out.vertices', function() {
73475                         if (map.editableDataEnabled() && !_isTransformed) {
73476                             var hover = event.relatedTarget && event.relatedTarget.__data__;
73477                             surface.call(drawVertices.drawHover, context.graph(), hover, map.extent());
73478                             dispatch$1.call('drawn', this, { full: false });
73479                         }
73480                     });
73481
73482                 var detected = utilDetect();
73483
73484                 // only WebKit supports gesture events
73485                 if ('GestureEvent' in window &&
73486                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
73487                     // but we only need to do this on desktop Safari anyway. – #7694
73488                     !detected.isMobileWebKit) {
73489
73490                     // Desktop Safari sends gesture events for multitouch trackpad pinches.
73491                     // We can listen for these and translate them into map zooms.
73492                     surface
73493                         .on('gesturestart.surface', function() {
73494                             event.preventDefault();
73495                             _gestureTransformStart = projection.transform();
73496                         })
73497                         .on('gesturechange.surface', gestureChange);
73498                 }
73499
73500                 // must call after surface init
73501                 updateAreaFill();
73502
73503                 _doubleUpHandler.on('doubleUp.map', function(p0) {
73504                     if (!_dblClickZoomEnabled) { return; }
73505
73506                     // don't zoom if targeting something other than the map itself
73507                     if (typeof event.target.__data__ === 'object' &&
73508                         // or area fills
73509                         !select(event.target).classed('fill')) { return; }
73510
73511                     var zoomOut = event.shiftKey;
73512
73513                     var t = projection.transform();
73514
73515                     var p1 = t.invert(p0);
73516
73517                     t = t.scale(zoomOut ? 0.5 : 2);
73518
73519                     t.x = p0[0] - p1[0] * t.k;
73520                     t.y = p0[1] - p1[1] * t.k;
73521
73522                     map.transformEase(t);
73523                 });
73524
73525                 context.on('enter.map',  function() {
73526                     if (!map.editableDataEnabled(true /* skip zoom check */)) { return; }
73527
73528                     // redraw immediately any objects affected by a change in selectedIDs.
73529                     var graph = context.graph();
73530                     var selectedAndParents = {};
73531                     context.selectedIDs().forEach(function(id) {
73532                         var entity = graph.hasEntity(id);
73533                         if (entity) {
73534                             selectedAndParents[entity.id] = entity;
73535                             if (entity.type === 'node') {
73536                                 graph.parentWays(entity).forEach(function(parent) {
73537                                     selectedAndParents[parent.id] = parent;
73538                                 });
73539                             }
73540                         }
73541                     });
73542                     var data = Object.values(selectedAndParents);
73543                     var filter = function(d) { return d.id in selectedAndParents; };
73544
73545                     data = context.features().filter(data, graph);
73546
73547                     surface
73548                         .call(drawVertices.drawSelected, graph, map.extent())
73549                         .call(drawLines, graph, data, filter)
73550                         .call(drawAreas, graph, data, filter)
73551                         .call(drawMidpoints, graph, data, filter, map.trimmedExtent());
73552
73553                     dispatch$1.call('drawn', this, { full: false });
73554
73555                     // redraw everything else later
73556                     scheduleRedraw();
73557                 });
73558
73559                 map.dimensions(utilGetDimensions(selection));
73560             }
73561
73562
73563             function zoomEventFilter() {
73564                 // Fix for #2151, (see also d3/d3-zoom#60, d3/d3-brush#18)
73565                 // Intercept `mousedown` and check if there is an orphaned zoom gesture.
73566                 // This can happen if a previous `mousedown` occurred without a `mouseup`.
73567                 // If we detect this, dispatch `mouseup` to complete the orphaned gesture,
73568                 // so that d3-zoom won't stop propagation of new `mousedown` events.
73569                 if (event.type === 'mousedown') {
73570                     var hasOrphan = false;
73571                     var listeners = window.__on;
73572                     for (var i = 0; i < listeners.length; i++) {
73573                         var listener = listeners[i];
73574                         if (listener.name === 'zoom' && listener.type === 'mouseup') {
73575                             hasOrphan = true;
73576                             break;
73577                         }
73578                     }
73579                     if (hasOrphan) {
73580                         var event$1 = window.CustomEvent;
73581                         if (event$1) {
73582                             event$1 = new event$1('mouseup');
73583                         } else {
73584                             event$1 = window.document.createEvent('Event');
73585                             event$1.initEvent('mouseup', false, false);
73586                         }
73587                         // Event needs to be dispatched with an event.view property.
73588                         event$1.view = window;
73589                         window.dispatchEvent(event$1);
73590                     }
73591                 }
73592
73593                 return event.button !== 2;   // ignore right clicks
73594             }
73595
73596
73597             function pxCenter() {
73598                 return [_dimensions[0] / 2, _dimensions[1] / 2];
73599             }
73600
73601
73602             function drawEditable(difference, extent) {
73603                 var mode = context.mode();
73604                 var graph = context.graph();
73605                 var features = context.features();
73606                 var all = context.history().intersects(map.extent());
73607                 var fullRedraw = false;
73608                 var data;
73609                 var set;
73610                 var filter;
73611                 var applyFeatureLayerFilters = true;
73612
73613                 if (map.isInWideSelection()) {
73614                     data = [];
73615                     utilEntityAndDeepMemberIDs(mode.selectedIDs(), context.graph()).forEach(function(id) {
73616                         var entity = context.hasEntity(id);
73617                         if (entity) { data.push(entity); }
73618                     });
73619                     fullRedraw = true;
73620                     filter = utilFunctor(true);
73621                     // selected features should always be visible, so we can skip filtering
73622                     applyFeatureLayerFilters = false;
73623
73624                 } else if (difference) {
73625                     var complete = difference.complete(map.extent());
73626                     data = Object.values(complete).filter(Boolean);
73627                     set = new Set(Object.keys(complete));
73628                     filter = function(d) { return set.has(d.id); };
73629                     features.clear(data);
73630
73631                 } else {
73632                     // force a full redraw if gatherStats detects that a feature
73633                     // should be auto-hidden (e.g. points or buildings)..
73634                     if (features.gatherStats(all, graph, _dimensions)) {
73635                         extent = undefined;
73636                     }
73637
73638                     if (extent) {
73639                         data = context.history().intersects(map.extent().intersection(extent));
73640                         set = new Set(data.map(function(entity) { return entity.id; }));
73641                         filter = function(d) { return set.has(d.id); };
73642
73643                     } else {
73644                         data = all;
73645                         fullRedraw = true;
73646                         filter = utilFunctor(true);
73647                     }
73648                 }
73649
73650                 if (applyFeatureLayerFilters) {
73651                     data = features.filter(data, graph);
73652                 } else {
73653                     context.features().resetStats();
73654                 }
73655
73656                 if (mode && mode.id === 'select') {
73657                     // update selected vertices - the user might have just double-clicked a way,
73658                     // creating a new vertex, triggering a partial redraw without a mode change
73659                     surface.call(drawVertices.drawSelected, graph, map.extent());
73660                 }
73661
73662                 surface
73663                     .call(drawVertices, graph, data, filter, map.extent(), fullRedraw)
73664                     .call(drawLines, graph, data, filter)
73665                     .call(drawAreas, graph, data, filter)
73666                     .call(drawMidpoints, graph, data, filter, map.trimmedExtent())
73667                     .call(drawLabels, graph, data, filter, _dimensions, fullRedraw)
73668                     .call(drawPoints, graph, data, filter);
73669
73670                 dispatch$1.call('drawn', this, {full: true});
73671             }
73672
73673             map.init = function() {
73674                 drawLayers = svgLayers(projection, context);
73675                 drawPoints = svgPoints(projection, context);
73676                 drawVertices = svgVertices(projection, context);
73677                 drawLines = svgLines(projection, context);
73678                 drawAreas = svgAreas(projection, context);
73679                 drawMidpoints = svgMidpoints(projection, context);
73680                 drawLabels = svgLabels(projection, context);
73681             };
73682
73683             function editOff() {
73684                 context.features().resetStats();
73685                 surface.selectAll('.layer-osm *').remove();
73686                 surface.selectAll('.layer-touch:not(.markers) *').remove();
73687
73688                 var allowed = {
73689                     'browse': true,
73690                     'save': true,
73691                     'select-note': true,
73692                     'select-data': true,
73693                     'select-error': true
73694                 };
73695
73696                 var mode = context.mode();
73697                 if (mode && !allowed[mode.id]) {
73698                     context.enter(modeBrowse(context));
73699                 }
73700
73701                 dispatch$1.call('drawn', this, {full: true});
73702             }
73703
73704
73705
73706
73707
73708             function gestureChange() {
73709                 // Remap Safari gesture events to wheel events - #5492
73710                 // We want these disabled most places, but enabled for zoom/unzoom on map surface
73711                 // https://developer.mozilla.org/en-US/docs/Web/API/GestureEvent
73712                 var e = event;
73713                 e.preventDefault();
73714
73715                 var props = {
73716                     deltaMode: 0,    // dummy values to ignore in zoomPan
73717                     deltaY: 1,       // dummy values to ignore in zoomPan
73718                     clientX: e.clientX,
73719                     clientY: e.clientY,
73720                     screenX: e.screenX,
73721                     screenY: e.screenY,
73722                     x: e.x,
73723                     y: e.y
73724                 };
73725
73726                 var e2 = new WheelEvent('wheel', props);
73727                 e2._scale = e.scale;         // preserve the original scale
73728                 e2._rotation = e.rotation;   // preserve the original rotation
73729
73730                 _selection.node().dispatchEvent(e2);
73731             }
73732
73733
73734             function zoomPan(manualEvent) {
73735                 var event$1 = (manualEvent || event);
73736                 var source = event$1.sourceEvent;
73737                 var eventTransform = event$1.transform;
73738                 var x = eventTransform.x;
73739                 var y = eventTransform.y;
73740                 var k = eventTransform.k;
73741
73742                 // Special handling of 'wheel' events:
73743                 // They might be triggered by the user scrolling the mouse wheel,
73744                 // or 2-finger pinch/zoom gestures, the transform may need adjustment.
73745                 if (source && source.type === 'wheel') {
73746
73747                     // assume that the gesture is already handled by pointer events
73748                     if (_pointerDown) { return; }
73749
73750                     var detected = utilDetect();
73751                     var dX = source.deltaX;
73752                     var dY = source.deltaY;
73753                     var x2 = x;
73754                     var y2 = y;
73755                     var k2 = k;
73756                     var t0, p0, p1;
73757
73758                     // Normalize mousewheel scroll speed (Firefox) - #3029
73759                     // If wheel delta is provided in LINE units, recalculate it in PIXEL units
73760                     // We are essentially redoing the calculations that occur here:
73761                     //   https://github.com/d3/d3-zoom/blob/78563a8348aa4133b07cac92e2595c2227ca7cd7/src/zoom.js#L203
73762                     // See this for more info:
73763                     //   https://github.com/basilfx/normalize-wheel/blob/master/src/normalizeWheel.js
73764                     if (source.deltaMode === 1 /* LINE */) {
73765                         // Convert from lines to pixels, more if the user is scrolling fast.
73766                         // (I made up the exp function to roughly match Firefox to what Chrome does)
73767                         // These numbers should be floats, because integers are treated as pan gesture below.
73768                         var lines = Math.abs(source.deltaY);
73769                         var sign = (source.deltaY > 0) ? 1 : -1;
73770                         dY = sign * clamp(
73771                             Math.exp((lines - 1) * 0.75) * 4.000244140625,
73772                             4.000244140625,    // min
73773                             350.000244140625   // max
73774                         );
73775
73776                         // On Firefox Windows and Linux we always get +/- the scroll line amount (default 3)
73777                         // There doesn't seem to be any scroll accelleration.
73778                         // This multiplier increases the speed a little bit - #5512
73779                         if (detected.os !== 'mac') {
73780                             dY *= 5;
73781                         }
73782
73783                         // recalculate x2,y2,k2
73784                         t0 = _isTransformed ? _transformLast : _transformStart;
73785                         p0 = _getMouseCoords(source);
73786                         p1 = t0.invert(p0);
73787                         k2 = t0.k * Math.pow(2, -dY / 500);
73788                         k2 = clamp(k2, kMin, kMax);
73789                         x2 = p0[0] - p1[0] * k2;
73790                         y2 = p0[1] - p1[1] * k2;
73791
73792                     // 2 finger map pinch zooming (Safari) - #5492
73793                     // These are fake `wheel` events we made from Safari `gesturechange` events..
73794                     } else if (source._scale) {
73795                         // recalculate x2,y2,k2
73796                         t0 = _gestureTransformStart;
73797                         p0 = _getMouseCoords(source);
73798                         p1 = t0.invert(p0);
73799                         k2 = t0.k * source._scale;
73800                         k2 = clamp(k2, kMin, kMax);
73801                         x2 = p0[0] - p1[0] * k2;
73802                         y2 = p0[1] - p1[1] * k2;
73803
73804                     // 2 finger map pinch zooming (all browsers except Safari) - #5492
73805                     // Pinch zooming via the `wheel` event will always have:
73806                     // - `ctrlKey = true`
73807                     // - `deltaY` is not round integer pixels (ignore `deltaX`)
73808                     } else if (source.ctrlKey && !isInteger(dY)) {
73809                         dY *= 6;   // slightly scale up whatever the browser gave us
73810
73811                         // recalculate x2,y2,k2
73812                         t0 = _isTransformed ? _transformLast : _transformStart;
73813                         p0 = _getMouseCoords(source);
73814                         p1 = t0.invert(p0);
73815                         k2 = t0.k * Math.pow(2, -dY / 500);
73816                         k2 = clamp(k2, kMin, kMax);
73817                         x2 = p0[0] - p1[0] * k2;
73818                         y2 = p0[1] - p1[1] * k2;
73819
73820                     // Trackpad scroll zooming with shift or alt/option key down
73821                     } else if ((source.altKey || source.shiftKey) && isInteger(dY)) {
73822                         // recalculate x2,y2,k2
73823                         t0 = _isTransformed ? _transformLast : _transformStart;
73824                         p0 = _getMouseCoords(source);
73825                         p1 = t0.invert(p0);
73826                         k2 = t0.k * Math.pow(2, -dY / 500);
73827                         k2 = clamp(k2, kMin, kMax);
73828                         x2 = p0[0] - p1[0] * k2;
73829                         y2 = p0[1] - p1[1] * k2;
73830
73831                     // 2 finger map panning (Mac only, all browsers) - #5492, #5512
73832                     // Panning via the `wheel` event will always have:
73833                     // - `ctrlKey = false`
73834                     // - `deltaX`,`deltaY` are round integer pixels
73835                     } else if (detected.os === 'mac' && !source.ctrlKey && isInteger(dX) && isInteger(dY)) {
73836                         p1 = projection.translate();
73837                         x2 = p1[0] - dX;
73838                         y2 = p1[1] - dY;
73839                         k2 = projection.scale();
73840                         k2 = clamp(k2, kMin, kMax);
73841                     }
73842
73843                     // something changed - replace the event transform
73844                     if (x2 !== x || y2 !== y || k2 !== k) {
73845                         x = x2;
73846                         y = y2;
73847                         k = k2;
73848                         eventTransform = identity$2.translate(x2, y2).scale(k2);
73849                         if (_zoomerPanner._transform) {
73850                             // utilZoomPan interface
73851                             _zoomerPanner._transform(eventTransform);
73852                         } else {
73853                             // d3_zoom interface
73854                             _selection.node().__zoom = eventTransform;
73855                         }
73856                     }
73857
73858                 }
73859
73860                 if (_transformStart.x === x &&
73861                     _transformStart.y === y &&
73862                     _transformStart.k === k) {
73863                     return;  // no change
73864                 }
73865
73866                 var withinEditableZoom = map.withinEditableZoom();
73867                 if (_lastWithinEditableZoom !== withinEditableZoom) {
73868                     if (_lastWithinEditableZoom !== undefined) {
73869                         // notify that the map zoomed in or out over the editable zoom threshold
73870                         dispatch$1.call('crossEditableZoom', this, withinEditableZoom);
73871                     }
73872                     _lastWithinEditableZoom = withinEditableZoom;
73873                 }
73874
73875                 if (geoScaleToZoom(k, TILESIZE) < _minzoom) {
73876                     surface.interrupt();
73877                     dispatch$1.call('hitMinZoom', this, map);
73878                     setCenterZoom(map.center(), context.minEditableZoom(), 0, true);
73879                     scheduleRedraw();
73880                     dispatch$1.call('move', this, map);
73881                     return;
73882                 }
73883
73884                 projection.transform(eventTransform);
73885
73886                 var scale = k / _transformStart.k;
73887                 var tX = (x / scale - _transformStart.x) * scale;
73888                 var tY = (y / scale - _transformStart.y) * scale;
73889
73890                 if (context.inIntro()) {
73891                     curtainProjection.transform({
73892                         x: x - tX,
73893                         y: y - tY,
73894                         k: k
73895                     });
73896                 }
73897
73898                 if (source) {
73899                     _lastPointerEvent = event$1;
73900                 }
73901                 _isTransformed = true;
73902                 _transformLast = eventTransform;
73903                 utilSetTransform(supersurface, tX, tY, scale);
73904                 scheduleRedraw();
73905
73906                 dispatch$1.call('move', this, map);
73907
73908
73909                 function isInteger(val) {
73910                     return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
73911                 }
73912             }
73913
73914
73915             function resetTransform() {
73916                 if (!_isTransformed) { return false; }
73917
73918                 utilSetTransform(supersurface, 0, 0);
73919                 _isTransformed = false;
73920                 if (context.inIntro()) {
73921                     curtainProjection.transform(projection.transform());
73922                 }
73923                 return true;
73924             }
73925
73926
73927             function redraw(difference, extent) {
73928                 if (surface.empty() || !_redrawEnabled) { return; }
73929
73930                 // If we are in the middle of a zoom/pan, we can't do differenced redraws.
73931                 // It would result in artifacts where differenced entities are redrawn with
73932                 // one transform and unchanged entities with another.
73933                 if (resetTransform()) {
73934                     difference = extent = undefined;
73935                 }
73936
73937                 var zoom = map.zoom();
73938                 var z = String(~~zoom);
73939
73940                 if (surface.attr('data-zoom') !== z) {
73941                     surface.attr('data-zoom', z);
73942                 }
73943
73944                 // class surface as `lowzoom` around z17-z18.5 (based on latitude)
73945                 var lat = map.center()[1];
73946                 var lowzoom = linear$2()
73947                     .domain([-60, 0, 60])
73948                     .range([17, 18.5, 17])
73949                     .clamp(true);
73950
73951                 surface
73952                     .classed('low-zoom', zoom <= lowzoom(lat));
73953
73954
73955                 if (!difference) {
73956                     supersurface.call(context.background());
73957                     wrapper.call(drawLayers);
73958                 }
73959
73960                 // OSM
73961                 if (map.editableDataEnabled() || map.isInWideSelection()) {
73962                     context.loadTiles(projection);
73963                     drawEditable(difference, extent);
73964                 } else {
73965                     editOff();
73966                 }
73967
73968                 _transformStart = projection.transform();
73969
73970                 return map;
73971             }
73972
73973
73974
73975             var immediateRedraw = function(difference, extent) {
73976                 if (!difference && !extent) { cancelPendingRedraw(); }
73977                 redraw(difference, extent);
73978             };
73979
73980
73981             map.lastPointerEvent = function() {
73982                 return _lastPointerEvent;
73983             };
73984
73985
73986             map.mouse = function() {
73987                 var event$1 = _lastPointerEvent || event;
73988                 if (event$1) {
73989                     var s;
73990                     while ((s = event$1.sourceEvent)) { event$1 = s; }
73991                     return _getMouseCoords(event$1);
73992                 }
73993                 return null;
73994             };
73995
73996
73997             // returns Lng/Lat
73998             map.mouseCoordinates = function() {
73999                 var coord = map.mouse() || pxCenter();
74000                 return projection.invert(coord);
74001             };
74002
74003
74004             map.dblclickZoomEnable = function(val) {
74005                 if (!arguments.length) { return _dblClickZoomEnabled; }
74006                 _dblClickZoomEnabled = val;
74007                 return map;
74008             };
74009
74010
74011             map.redrawEnable = function(val) {
74012                 if (!arguments.length) { return _redrawEnabled; }
74013                 _redrawEnabled = val;
74014                 return map;
74015             };
74016
74017
74018             map.isTransformed = function() {
74019                 return _isTransformed;
74020             };
74021
74022
74023             function setTransform(t2, duration, force) {
74024                 var t = projection.transform();
74025                 if (!force && t2.k === t.k && t2.x === t.x && t2.y === t.y) { return false; }
74026
74027                 if (duration) {
74028                     _selection
74029                         .transition()
74030                         .duration(duration)
74031                         .on('start', function() { map.startEase(); })
74032                         .call(_zoomerPanner.transform, identity$2.translate(t2.x, t2.y).scale(t2.k));
74033                 } else {
74034                     projection.transform(t2);
74035                     _transformStart = t2;
74036                     _selection.call(_zoomerPanner.transform, _transformStart);
74037                 }
74038
74039                 return true;
74040             }
74041
74042
74043             function setCenterZoom(loc2, z2, duration, force) {
74044                 var c = map.center();
74045                 var z = map.zoom();
74046                 if (loc2[0] === c[0] && loc2[1] === c[1] && z2 === z && !force) { return false; }
74047
74048                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74049
74050                 var k2 = clamp(geoZoomToScale(z2, TILESIZE), kMin, kMax);
74051                 proj.scale(k2);
74052
74053                 var t = proj.translate();
74054                 var point = proj(loc2);
74055
74056                 var center = pxCenter();
74057                 t[0] += center[0] - point[0];
74058                 t[1] += center[1] - point[1];
74059
74060                 return setTransform(identity$2.translate(t[0], t[1]).scale(k2), duration, force);
74061             }
74062
74063
74064             map.pan = function(delta, duration) {
74065                 var t = projection.translate();
74066                 var k = projection.scale();
74067
74068                 t[0] += delta[0];
74069                 t[1] += delta[1];
74070
74071                 if (duration) {
74072                     _selection
74073                         .transition()
74074                         .duration(duration)
74075                         .on('start', function() { map.startEase(); })
74076                         .call(_zoomerPanner.transform, identity$2.translate(t[0], t[1]).scale(k));
74077                 } else {
74078                     projection.translate(t);
74079                     _transformStart = projection.transform();
74080                     _selection.call(_zoomerPanner.transform, _transformStart);
74081                     dispatch$1.call('move', this, map);
74082                     immediateRedraw();
74083                 }
74084
74085                 return map;
74086             };
74087
74088
74089             map.dimensions = function(val) {
74090                 if (!arguments.length) { return _dimensions; }
74091
74092                 _dimensions = val;
74093                 drawLayers.dimensions(_dimensions);
74094                 context.background().dimensions(_dimensions);
74095                 projection.clipExtent([[0, 0], _dimensions]);
74096                 _getMouseCoords = utilFastMouse(supersurface.node());
74097
74098                 scheduleRedraw();
74099                 return map;
74100             };
74101
74102
74103             function zoomIn(delta) {
74104                 setCenterZoom(map.center(), ~~map.zoom() + delta, 250, true);
74105             }
74106
74107             function zoomOut(delta) {
74108                 setCenterZoom(map.center(), ~~map.zoom() - delta, 250, true);
74109             }
74110
74111             map.zoomIn = function() { zoomIn(1); };
74112             map.zoomInFurther = function() { zoomIn(4); };
74113             map.canZoomIn = function() { return map.zoom() < maxZoom; };
74114
74115             map.zoomOut = function() { zoomOut(1); };
74116             map.zoomOutFurther = function() { zoomOut(4); };
74117             map.canZoomOut = function() { return map.zoom() > minZoom; };
74118
74119             map.center = function(loc2) {
74120                 if (!arguments.length) {
74121                     return projection.invert(pxCenter());
74122                 }
74123
74124                 if (setCenterZoom(loc2, map.zoom())) {
74125                     dispatch$1.call('move', this, map);
74126                 }
74127
74128                 scheduleRedraw();
74129                 return map;
74130             };
74131
74132             map.unobscuredCenterZoomEase = function(loc, zoom) {
74133                 var offset = map.unobscuredOffsetPx();
74134
74135                 var proj = geoRawMercator().transform(projection.transform());  // copy projection
74136                 // use the target zoom to calculate the offset center
74137                 proj.scale(geoZoomToScale(zoom, TILESIZE));
74138
74139                 var locPx = proj(loc);
74140                 var offsetLocPx = [locPx[0] + offset[0], locPx[1] + offset[1]];
74141                 var offsetLoc = proj.invert(offsetLocPx);
74142
74143                 map.centerZoomEase(offsetLoc, zoom);
74144             };
74145
74146             map.unobscuredOffsetPx = function() {
74147                 var openPane = context.container().select('.map-panes .map-pane.shown');
74148                 if (!openPane.empty()) {
74149                     return [openPane.node().offsetWidth/2, 0];
74150                 }
74151                 return [0, 0];
74152             };
74153
74154             map.zoom = function(z2) {
74155                 if (!arguments.length) {
74156                     return Math.max(geoScaleToZoom(projection.scale(), TILESIZE), 0);
74157                 }
74158
74159                 if (z2 < _minzoom) {
74160                     surface.interrupt();
74161                     dispatch$1.call('hitMinZoom', this, map);
74162                     z2 = context.minEditableZoom();
74163                 }
74164
74165                 if (setCenterZoom(map.center(), z2)) {
74166                     dispatch$1.call('move', this, map);
74167                 }
74168
74169                 scheduleRedraw();
74170                 return map;
74171             };
74172
74173
74174             map.centerZoom = function(loc2, z2) {
74175                 if (setCenterZoom(loc2, z2)) {
74176                     dispatch$1.call('move', this, map);
74177                 }
74178
74179                 scheduleRedraw();
74180                 return map;
74181             };
74182
74183
74184             map.zoomTo = function(entity) {
74185                 var extent = entity.extent(context.graph());
74186                 if (!isFinite(extent.area())) { return map; }
74187
74188                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74189                 return map.centerZoom(extent.center(), z2);
74190             };
74191
74192
74193             map.centerEase = function(loc2, duration) {
74194                 duration = duration || 250;
74195                 setCenterZoom(loc2, map.zoom(), duration);
74196                 return map;
74197             };
74198
74199
74200             map.zoomEase = function(z2, duration) {
74201                 duration = duration || 250;
74202                 setCenterZoom(map.center(), z2, duration, false);
74203                 return map;
74204             };
74205
74206
74207             map.centerZoomEase = function(loc2, z2, duration) {
74208                 duration = duration || 250;
74209                 setCenterZoom(loc2, z2, duration, false);
74210                 return map;
74211             };
74212
74213
74214             map.transformEase = function(t2, duration) {
74215                 duration = duration || 250;
74216                 setTransform(t2, duration, false /* don't force */);
74217                 return map;
74218             };
74219
74220
74221             map.zoomToEase = function(obj, duration) {
74222                 var extent;
74223                 if (Array.isArray(obj)) {
74224                     obj.forEach(function(entity) {
74225                         var entityExtent = entity.extent(context.graph());
74226                         if (!extent) {
74227                             extent = entityExtent;
74228                         } else {
74229                             extent = extent.extend(entityExtent);
74230                         }
74231                     });
74232                 } else {
74233                     extent = obj.extent(context.graph());
74234                 }
74235                 if (!isFinite(extent.area())) { return map; }
74236
74237                 var z2 = clamp(map.trimmedExtentZoom(extent), 0, 20);
74238                 return map.centerZoomEase(extent.center(), z2, duration);
74239             };
74240
74241
74242             map.startEase = function() {
74243                 utilBindOnce(surface, _pointerPrefix + 'down.ease', function() {
74244                     map.cancelEase();
74245                 });
74246                 return map;
74247             };
74248
74249
74250             map.cancelEase = function() {
74251                 _selection.interrupt();
74252                 return map;
74253             };
74254
74255
74256             map.extent = function(val) {
74257                 if (!arguments.length) {
74258                     return new geoExtent(
74259                         projection.invert([0, _dimensions[1]]),
74260                         projection.invert([_dimensions[0], 0])
74261                     );
74262                 } else {
74263                     var extent = geoExtent(val);
74264                     map.centerZoom(extent.center(), map.extentZoom(extent));
74265                 }
74266             };
74267
74268
74269             map.trimmedExtent = function(val) {
74270                 if (!arguments.length) {
74271                     var headerY = 71;
74272                     var footerY = 30;
74273                     var pad = 10;
74274                     return new geoExtent(
74275                         projection.invert([pad, _dimensions[1] - footerY - pad]),
74276                         projection.invert([_dimensions[0] - pad, headerY + pad])
74277                     );
74278                 } else {
74279                     var extent = geoExtent(val);
74280                     map.centerZoom(extent.center(), map.trimmedExtentZoom(extent));
74281                 }
74282             };
74283
74284
74285             function calcExtentZoom(extent, dim) {
74286                 var tl = projection([extent[0][0], extent[1][1]]);
74287                 var br = projection([extent[1][0], extent[0][1]]);
74288
74289                 // Calculate maximum zoom that fits extent
74290                 var hFactor = (br[0] - tl[0]) / dim[0];
74291                 var vFactor = (br[1] - tl[1]) / dim[1];
74292                 var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
74293                 var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
74294                 var newZoom = map.zoom() - Math.max(hZoomDiff, vZoomDiff);
74295
74296                 return newZoom;
74297             }
74298
74299
74300             map.extentZoom = function(val) {
74301                 return calcExtentZoom(geoExtent(val), _dimensions);
74302             };
74303
74304
74305             map.trimmedExtentZoom = function(val) {
74306                 var trimY = 120;
74307                 var trimX = 40;
74308                 var trimmed = [_dimensions[0] - trimX, _dimensions[1] - trimY];
74309                 return calcExtentZoom(geoExtent(val), trimmed);
74310             };
74311
74312
74313             map.withinEditableZoom = function() {
74314                 return map.zoom() >= context.minEditableZoom();
74315             };
74316
74317
74318             map.isInWideSelection = function() {
74319                 return !map.withinEditableZoom() && context.selectedIDs().length;
74320             };
74321
74322
74323             map.editableDataEnabled = function(skipZoomCheck) {
74324
74325                 var layer = context.layers().layer('osm');
74326                 if (!layer || !layer.enabled()) { return false; }
74327
74328                 return skipZoomCheck || map.withinEditableZoom();
74329             };
74330
74331
74332             map.notesEditable = function() {
74333                 var layer = context.layers().layer('notes');
74334                 if (!layer || !layer.enabled()) { return false; }
74335
74336                 return map.withinEditableZoom();
74337             };
74338
74339
74340             map.minzoom = function(val) {
74341                 if (!arguments.length) { return _minzoom; }
74342                 _minzoom = val;
74343                 return map;
74344             };
74345
74346
74347             map.toggleHighlightEdited = function() {
74348                 surface.classed('highlight-edited', !surface.classed('highlight-edited'));
74349                 map.pan([0,0]);  // trigger a redraw
74350                 dispatch$1.call('changeHighlighting', this);
74351             };
74352
74353
74354             map.areaFillOptions = ['wireframe', 'partial', 'full'];
74355
74356             map.activeAreaFill = function(val) {
74357                 if (!arguments.length) { return corePreferences('area-fill') || 'partial'; }
74358
74359                 corePreferences('area-fill', val);
74360                 if (val !== 'wireframe') {
74361                     corePreferences('area-fill-toggle', val);
74362                 }
74363                 updateAreaFill();
74364                 map.pan([0,0]);  // trigger a redraw
74365                 dispatch$1.call('changeAreaFill', this);
74366                 return map;
74367             };
74368
74369             map.toggleWireframe = function() {
74370
74371                 var activeFill = map.activeAreaFill();
74372
74373                 if (activeFill === 'wireframe') {
74374                     activeFill = corePreferences('area-fill-toggle') || 'partial';
74375                 } else {
74376                     activeFill = 'wireframe';
74377                 }
74378
74379                 map.activeAreaFill(activeFill);
74380             };
74381
74382             function updateAreaFill() {
74383                 var activeFill = map.activeAreaFill();
74384                 map.areaFillOptions.forEach(function(opt) {
74385                     surface.classed('fill-' + opt, Boolean(opt === activeFill));
74386                 });
74387             }
74388
74389
74390             map.layers = function () { return drawLayers; };
74391
74392
74393             map.doubleUpHandler = function() {
74394                 return _doubleUpHandler;
74395             };
74396
74397
74398             return utilRebind(map, dispatch$1, 'on');
74399         }
74400
74401         function rendererPhotos(context) {
74402             var dispatch$1 = dispatch('change');
74403             var _layerIDs = ['streetside', 'mapillary', 'mapillary-map-features', 'mapillary-signs', 'openstreetcam'];
74404             var _allPhotoTypes = ['flat', 'panoramic'];
74405             var _shownPhotoTypes = _allPhotoTypes.slice();   // shallow copy
74406
74407             function photos() {}
74408
74409             function updateStorage() {
74410                 if (window.mocha) { return; }
74411
74412                 var hash = utilStringQs(window.location.hash);
74413                 var enabled = context.layers().all().filter(function(d) {
74414                     return _layerIDs.indexOf(d.id) !== -1 && d.layer && d.layer.supported() && d.layer.enabled();
74415                 }).map(function(d) {
74416                     return d.id;
74417                 });
74418                 if (enabled.length) {
74419                     hash.photo_overlay = enabled.join(',');
74420                 } else {
74421                     delete hash.photo_overlay;
74422                 }
74423                 window.location.replace('#' + utilQsString(hash, true));
74424             }
74425
74426             photos.overlayLayerIDs = function() {
74427                 return _layerIDs;
74428             };
74429
74430             photos.allPhotoTypes = function() {
74431                 return _allPhotoTypes;
74432             };
74433
74434             function showsLayer(id) {
74435                 var layer = context.layers().layer(id);
74436                 return layer && layer.supported() && layer.enabled();
74437             }
74438
74439             photos.shouldFilterByPhotoType = function() {
74440                 return showsLayer('mapillary') ||
74441                     (showsLayer('streetside') && showsLayer('openstreetcam'));
74442             };
74443
74444             photos.showsPhotoType = function(val) {
74445                 if (!photos.shouldFilterByPhotoType()) { return true; }
74446
74447                 return _shownPhotoTypes.indexOf(val) !== -1;
74448             };
74449
74450             photos.showsFlat = function() {
74451                 return photos.showsPhotoType('flat');
74452             };
74453
74454             photos.showsPanoramic = function() {
74455                 return photos.showsPhotoType('panoramic');
74456             };
74457
74458             photos.togglePhotoType = function(val) {
74459                 var index = _shownPhotoTypes.indexOf(val);
74460                 if (index !== -1) {
74461                     _shownPhotoTypes.splice(index, 1);
74462                 } else {
74463                     _shownPhotoTypes.push(val);
74464                 }
74465                 dispatch$1.call('change', this);
74466                 return photos;
74467             };
74468
74469             photos.init = function() {
74470                 var hash = utilStringQs(window.location.hash);
74471                 if (hash.photo_overlay) {
74472                     var hashOverlayIDs = hash.photo_overlay.replace(/;/g, ',').split(',');
74473                     hashOverlayIDs.forEach(function(id) {
74474                         var layer = context.layers().layer(id);
74475                         if (layer) { layer.enabled(true); }
74476                     });
74477                 }
74478
74479                 context.layers().on('change.rendererPhotos', updateStorage);
74480             };
74481
74482             return utilRebind(photos, dispatch$1, 'on');
74483         }
74484
74485         function uiAccount(context) {
74486             var osm = context.connection();
74487
74488
74489             function update(selection) {
74490                 if (!osm) { return; }
74491
74492                 if (!osm.authenticated()) {
74493                     selection.selectAll('.userLink, .logoutLink')
74494                         .classed('hide', true);
74495                     return;
74496                 }
74497
74498                 osm.userDetails(function(err, details) {
74499                     var userLink = selection.select('.userLink'),
74500                         logoutLink = selection.select('.logoutLink');
74501
74502                     userLink.html('');
74503                     logoutLink.html('');
74504
74505                     if (err || !details) { return; }
74506
74507                     selection.selectAll('.userLink, .logoutLink')
74508                         .classed('hide', false);
74509
74510                     // Link
74511                     userLink.append('a')
74512                         .attr('href', osm.userURL(details.display_name))
74513                         .attr('target', '_blank');
74514
74515                     // Add thumbnail or dont
74516                     if (details.image_url) {
74517                         userLink.append('img')
74518                             .attr('class', 'icon pre-text user-icon')
74519                             .attr('src', details.image_url);
74520                     } else {
74521                         userLink
74522                             .call(svgIcon('#iD-icon-avatar', 'pre-text light'));
74523                     }
74524
74525                     // Add user name
74526                     userLink.append('span')
74527                         .attr('class', 'label')
74528                         .text(details.display_name);
74529
74530                     logoutLink.append('a')
74531                         .attr('class', 'logout')
74532                         .attr('href', '#')
74533                         .text(_t('logout'))
74534                         .on('click.logout', function() {
74535                             event.preventDefault();
74536                             osm.logout();
74537                         });
74538                 });
74539             }
74540
74541
74542             return function(selection) {
74543                 selection.append('li')
74544                     .attr('class', 'logoutLink')
74545                     .classed('hide', true);
74546
74547                 selection.append('li')
74548                     .attr('class', 'userLink')
74549                     .classed('hide', true);
74550
74551                 if (osm) {
74552                     osm.on('change.account', function() { update(selection); });
74553                     update(selection);
74554                 }
74555             };
74556         }
74557
74558         function uiAttribution(context) {
74559           var _selection = select(null);
74560
74561
74562           function render(selection, data, klass) {
74563             var div = selection.selectAll(("." + klass))
74564               .data([0]);
74565
74566             div = div.enter()
74567               .append('div')
74568               .attr('class', klass)
74569               .merge(div);
74570
74571
74572             var attributions = div.selectAll('.attribution')
74573               .data(data, function (d) { return d.id; });
74574
74575             attributions.exit()
74576               .remove();
74577
74578             attributions = attributions.enter()
74579               .append('span')
74580               .attr('class', 'attribution')
74581               .each(function (d, i, nodes) {
74582                 var attribution = select(nodes[i]);
74583
74584                 if (d.terms_html) {
74585                   attribution.html(d.terms_html);
74586                   return;
74587                 }
74588
74589                 if (d.terms_url) {
74590                   attribution = attribution
74591                     .append('a')
74592                     .attr('href', d.terms_url)
74593                     .attr('target', '_blank');
74594                 }
74595
74596                 var sourceID = d.id.replace(/\./g, '<TX_DOT>');
74597                 var terms_text = _t(("imagery." + sourceID + ".attribution.text"),
74598                   { default: d.terms_text || d.id || d.name() }
74599                 );
74600
74601                 if (d.icon && !d.overlay) {
74602                   attribution
74603                     .append('img')
74604                     .attr('class', 'source-image')
74605                     .attr('src', d.icon);
74606                 }
74607
74608                 attribution
74609                   .append('span')
74610                   .attr('class', 'attribution-text')
74611                   .text(terms_text);
74612               })
74613               .merge(attributions);
74614
74615
74616             var copyright = attributions.selectAll('.copyright-notice')
74617               .data(function (d) {
74618                 var notice = d.copyrightNotices(context.map().zoom(), context.map().extent());
74619                 return notice ? [notice] : [];
74620               });
74621
74622             copyright.exit()
74623               .remove();
74624
74625             copyright = copyright.enter()
74626               .append('span')
74627               .attr('class', 'copyright-notice')
74628               .merge(copyright);
74629
74630             copyright
74631               .text(String);
74632           }
74633
74634
74635           function update() {
74636             var baselayer = context.background().baseLayerSource();
74637             _selection
74638               .call(render, (baselayer ? [baselayer] : []), 'base-layer-attribution');
74639
74640             var z = context.map().zoom();
74641             var overlays = context.background().overlayLayerSources() || [];
74642             _selection
74643               .call(render, overlays.filter(function (s) { return s.validZoom(z); }), 'overlay-layer-attribution');
74644           }
74645
74646
74647           return function(selection) {
74648             _selection = selection;
74649
74650             context.background()
74651               .on('change.attribution', update);
74652
74653             context.map()
74654               .on('move.attribution', throttle(update, 400, { leading: false }));
74655
74656             update();
74657           };
74658         }
74659
74660         function uiContributors(context) {
74661             var osm = context.connection(),
74662                 debouncedUpdate = debounce(function() { update(); }, 1000),
74663                 limit = 4,
74664                 hidden = false,
74665                 wrap = select(null);
74666
74667
74668             function update() {
74669                 if (!osm) { return; }
74670
74671                 var users = {},
74672                     entities = context.history().intersects(context.map().extent());
74673
74674                 entities.forEach(function(entity) {
74675                     if (entity && entity.user) { users[entity.user] = true; }
74676                 });
74677
74678                 var u = Object.keys(users),
74679                     subset = u.slice(0, u.length > limit ? limit - 1 : limit);
74680
74681                 wrap.html('')
74682                     .call(svgIcon('#iD-icon-nearby', 'pre-text light'));
74683
74684                 var userList = select(document.createElement('span'));
74685
74686                 userList.selectAll()
74687                     .data(subset)
74688                     .enter()
74689                     .append('a')
74690                     .attr('class', 'user-link')
74691                     .attr('href', function(d) { return osm.userURL(d); })
74692                     .attr('target', '_blank')
74693                     .text(String);
74694
74695                 if (u.length > limit) {
74696                     var count = select(document.createElement('span'));
74697
74698                     count.append('a')
74699                         .attr('target', '_blank')
74700                         .attr('href', function() {
74701                             return osm.changesetsURL(context.map().center(), context.map().zoom());
74702                         })
74703                         .text(u.length - limit + 1);
74704
74705                     wrap.append('span')
74706                         .html(_t('contributors.truncated_list', { users: userList.html(), count: count.html() }));
74707
74708                 } else {
74709                     wrap.append('span')
74710                         .html(_t('contributors.list', { users: userList.html() }));
74711                 }
74712
74713                 if (!u.length) {
74714                     hidden = true;
74715                     wrap
74716                         .transition()
74717                         .style('opacity', 0);
74718
74719                 } else if (hidden) {
74720                     wrap
74721                         .transition()
74722                         .style('opacity', 1);
74723                 }
74724             }
74725
74726
74727             return function(selection) {
74728                 if (!osm) { return; }
74729                 wrap = selection;
74730                 update();
74731
74732                 osm.on('loaded.contributors', debouncedUpdate);
74733                 context.map().on('move.contributors', debouncedUpdate);
74734             };
74735         }
74736
74737         var _popoverID = 0;
74738
74739         function uiPopover(klass) {
74740             var _id = _popoverID++;
74741             var _anchorSelection = select(null);
74742             var popover = function(selection) {
74743                 _anchorSelection = selection;
74744                 selection.each(setup);
74745             };
74746             var _animation = utilFunctor(false);
74747             var _placement = utilFunctor('top'); // top, bottom, left, right
74748             var _alignment = utilFunctor('center');  // leading, center, trailing
74749             var _scrollContainer = utilFunctor(select(null));
74750             var _content;
74751             var _displayType = utilFunctor('');
74752             var _hasArrow = utilFunctor(true);
74753
74754             // use pointer events on supported platforms; fallback to mouse events
74755             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
74756
74757             popover.displayType = function(val) {
74758                 if (arguments.length) {
74759                     _displayType = utilFunctor(val);
74760                     return popover;
74761                 } else {
74762                     return _displayType;
74763                 }
74764             };
74765
74766             popover.hasArrow = function(val) {
74767                 if (arguments.length) {
74768                     _hasArrow = utilFunctor(val);
74769                     return popover;
74770                 } else {
74771                     return _hasArrow;
74772                 }
74773             };
74774
74775             popover.placement = function(val) {
74776                 if (arguments.length) {
74777                     _placement = utilFunctor(val);
74778                     return popover;
74779                 } else {
74780                     return _placement;
74781                 }
74782             };
74783
74784             popover.alignment = function(val) {
74785                 if (arguments.length) {
74786                     _alignment = utilFunctor(val);
74787                     return popover;
74788                 } else {
74789                     return _alignment;
74790                 }
74791             };
74792
74793             popover.scrollContainer = function(val) {
74794                 if (arguments.length) {
74795                     _scrollContainer = utilFunctor(val);
74796                     return popover;
74797                 } else {
74798                     return _scrollContainer;
74799                 }
74800             };
74801
74802             popover.content = function(val) {
74803                 if (arguments.length) {
74804                     _content = val;
74805                     return popover;
74806                 } else {
74807                     return _content;
74808                 }
74809             };
74810
74811             popover.isShown = function() {
74812                 var popoverSelection = _anchorSelection.select('.popover-' + _id);
74813                 return !popoverSelection.empty() && popoverSelection.classed('in');
74814             };
74815
74816             popover.show = function() {
74817                 _anchorSelection.each(show);
74818             };
74819
74820             popover.updateContent = function() {
74821                 _anchorSelection.each(updateContent);
74822             };
74823
74824             popover.hide = function() {
74825                 _anchorSelection.each(hide);
74826             };
74827
74828             popover.toggle = function() {
74829                 _anchorSelection.each(toggle);
74830             };
74831
74832             popover.destroy = function(selection, selector) {
74833                 // by default, just destroy the current popover
74834                 selector = selector || '.popover-' + _id;
74835
74836                 selection
74837                     .on(_pointerPrefix + 'enter.popover', null)
74838                     .on(_pointerPrefix + 'leave.popover', null)
74839                     .on(_pointerPrefix + 'up.popover', null)
74840                     .on(_pointerPrefix + 'down.popover', null)
74841                     .on('click.popover', null)
74842                     .attr('title', function() {
74843                         return this.getAttribute('data-original-title') || this.getAttribute('title');
74844                     })
74845                     .attr('data-original-title', null)
74846                     .selectAll(selector)
74847                     .remove();
74848             };
74849
74850
74851             popover.destroyAny = function(selection) {
74852                 selection.call(popover.destroy, '.popover');
74853             };
74854
74855             function setup() {
74856                 var anchor = select(this);
74857                 var animate = _animation.apply(this, arguments);
74858                 var popoverSelection = anchor.selectAll('.popover-' + _id)
74859                     .data([0]);
74860
74861
74862                 var enter = popoverSelection.enter()
74863                     .append('div')
74864                     .attr('class', 'popover popover-' + _id + ' ' + (klass ? klass : ''))
74865                     .classed('arrowed', _hasArrow.apply(this, arguments));
74866
74867                 enter
74868                     .append('div')
74869                     .attr('class', 'popover-arrow');
74870
74871                 enter
74872                     .append('div')
74873                     .attr('class', 'popover-inner');
74874
74875                 popoverSelection = enter
74876                     .merge(popoverSelection);
74877
74878                 if (animate) {
74879                     popoverSelection.classed('fade', true);
74880                 }
74881
74882                 var display = _displayType.apply(this, arguments);
74883
74884                 if (display === 'hover') {
74885                     var _lastNonMouseEnterTime;
74886                     anchor.on(_pointerPrefix + 'enter.popover', function() {
74887
74888                         if (event.pointerType) {
74889                             if (event.pointerType !== 'mouse') {
74890                                 _lastNonMouseEnterTime = event.timeStamp;
74891                                 // only allow hover behavior for mouse input
74892                                 return;
74893                             } else if (_lastNonMouseEnterTime &&
74894                                 event.timeStamp - _lastNonMouseEnterTime < 1500) {
74895                                 // HACK: iOS 13.4 sends an erroneous `mouse` type pointerenter
74896                                 // event for non-mouse interactions right after sending
74897                                 // the correct type pointerenter event. Workaround by discarding
74898                                 // any mouse event that occurs immediately after a non-mouse event.
74899                                 return;
74900                             }
74901                         }
74902
74903                         // don't show if buttons are pressed, e.g. during click and drag of map
74904                         if (event.buttons !== 0) { return; }
74905
74906                         show.apply(this, arguments);
74907                     });
74908                     anchor.on(_pointerPrefix + 'leave.popover', function() {
74909                         hide.apply(this, arguments);
74910                     });
74911
74912                 } else if (display === 'clickFocus') {
74913                     anchor
74914                         .on(_pointerPrefix + 'down.popover', function() {
74915                             event.preventDefault();
74916                             event.stopPropagation();
74917                         })
74918                         .on(_pointerPrefix + 'up.popover', function() {
74919                             event.preventDefault();
74920                             event.stopPropagation();
74921                         })
74922                         .on('click.popover', toggle);
74923
74924                     popoverSelection
74925                         .attr('tabindex', 0)
74926                         .on('blur.popover', function() {
74927                             anchor.each(function() {
74928                                 hide.apply(this, arguments);
74929                             });
74930                         });
74931                 }
74932             }
74933
74934
74935             function show() {
74936                 var anchor = select(this);
74937                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74938
74939                 if (popoverSelection.empty()) {
74940                     // popover was removed somehow, put it back
74941                     anchor.call(popover.destroy);
74942                     anchor.each(setup);
74943                     popoverSelection = anchor.selectAll('.popover-' + _id);
74944                 }
74945
74946                 popoverSelection.classed('in', true);
74947
74948                 var displayType = _displayType.apply(this, arguments);
74949                 if (displayType === 'clickFocus') {
74950                     anchor.classed('active', true);
74951                     popoverSelection.node().focus();
74952                 }
74953
74954                 anchor.each(updateContent);
74955             }
74956
74957             function updateContent() {
74958                 var anchor = select(this);
74959
74960                 if (_content) {
74961                     anchor.selectAll('.popover-' + _id + ' > .popover-inner')
74962                         .call(_content.apply(this, arguments));
74963                 }
74964
74965                 updatePosition.apply(this, arguments);
74966                 // hack: update multiple times to fix instances where the absolute offset is
74967                 // set before the dynamic popover size is calculated by the browser
74968                 updatePosition.apply(this, arguments);
74969                 updatePosition.apply(this, arguments);
74970             }
74971
74972
74973             function updatePosition() {
74974
74975                 var anchor = select(this);
74976                 var popoverSelection = anchor.selectAll('.popover-' + _id);
74977
74978                 var scrollContainer = _scrollContainer && _scrollContainer.apply(this, arguments);
74979                 var scrollNode = scrollContainer && !scrollContainer.empty() && scrollContainer.node();
74980                 var scrollLeft = scrollNode ? scrollNode.scrollLeft : 0;
74981                 var scrollTop = scrollNode ? scrollNode.scrollTop : 0;
74982
74983                 var placement = _placement.apply(this, arguments);
74984                 popoverSelection
74985                     .classed('left', false)
74986                     .classed('right', false)
74987                     .classed('top', false)
74988                     .classed('bottom', false)
74989                     .classed(placement, true);
74990
74991                 var alignment = _alignment.apply(this, arguments);
74992                 var alignFactor = 0.5;
74993                 if (alignment === 'leading') {
74994                     alignFactor = 0;
74995                 } else if (alignment === 'trailing') {
74996                     alignFactor = 1;
74997                 }
74998                 var anchorFrame = getFrame(anchor.node());
74999                 var popoverFrame = getFrame(popoverSelection.node());
75000                 var position;
75001
75002                 switch (placement) {
75003                     case 'top':
75004                     position = {
75005                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75006                         y: anchorFrame.y - popoverFrame.h
75007                     };
75008                     break;
75009                     case 'bottom':
75010                     position = {
75011                         x: anchorFrame.x + (anchorFrame.w - popoverFrame.w) * alignFactor,
75012                         y: anchorFrame.y + anchorFrame.h
75013                     };
75014                     break;
75015                     case 'left':
75016                     position = {
75017                         x: anchorFrame.x - popoverFrame.w,
75018                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75019                     };
75020                     break;
75021                     case 'right':
75022                     position = {
75023                         x: anchorFrame.x + anchorFrame.w,
75024                         y: anchorFrame.y + (anchorFrame.h - popoverFrame.h) * alignFactor
75025                     };
75026                     break;
75027                 }
75028
75029                 if (position) {
75030
75031                     if (scrollNode && (placement === 'top' || placement === 'bottom')) {
75032
75033                         var initialPosX = position.x;
75034
75035                         if (position.x + popoverFrame.w > scrollNode.offsetWidth - 10) {
75036                             position.x = scrollNode.offsetWidth - 10 - popoverFrame.w;
75037                         } else if (position.x < 10) {
75038                             position.x = 10;
75039                         }
75040
75041                         var arrow = anchor.selectAll('.popover-' + _id + ' > .popover-arrow');
75042                         // keep the arrow centered on the button, or as close as possible
75043                         var arrowPosX = Math.min(Math.max(popoverFrame.w / 2 - (position.x - initialPosX), 10), popoverFrame.w - 10);
75044                         arrow.style('left', ~~arrowPosX + 'px');
75045                     }
75046
75047                     popoverSelection.style('left', ~~position.x + 'px').style('top', ~~position.y + 'px');
75048                 } else {
75049                     popoverSelection.style('left', null).style('top', null);
75050                 }
75051
75052                 function getFrame(node) {
75053                     var positionStyle = select(node).style('position');
75054                     if (positionStyle === 'absolute' || positionStyle === 'static') {
75055                         return {
75056                             x: node.offsetLeft - scrollLeft,
75057                             y: node.offsetTop - scrollTop,
75058                             w: node.offsetWidth,
75059                             h: node.offsetHeight
75060                         };
75061                     } else {
75062                         return {
75063                             x: 0,
75064                             y: 0,
75065                             w: node.offsetWidth,
75066                             h: node.offsetHeight
75067                         };
75068                     }
75069                 }
75070             }
75071
75072
75073             function hide() {
75074                 var anchor = select(this);
75075                 if (_displayType.apply(this, arguments) === 'clickFocus') {
75076                     anchor.classed('active', false);
75077                 }
75078                 anchor.selectAll('.popover-' + _id).classed('in', false);
75079             }
75080
75081
75082             function toggle() {
75083                 if (select(this).select('.popover-' + _id).classed('in')) {
75084                     hide.apply(this, arguments);
75085                 } else {
75086                     show.apply(this, arguments);
75087                 }
75088             }
75089
75090
75091             return popover;
75092         }
75093
75094         function uiTooltip(klass) {
75095
75096             var tooltip = uiPopover((klass || '') + ' tooltip')
75097                 .displayType('hover');
75098
75099             var _title = function() {
75100                 var title = this.getAttribute('data-original-title');
75101                 if (title) {
75102                     return title;
75103                 } else {
75104                     title = this.getAttribute('title');
75105                     this.removeAttribute('title');
75106                     this.setAttribute('data-original-title', title);
75107                 }
75108                 return title;
75109             };
75110
75111             var _heading = utilFunctor(null);
75112             var _keys = utilFunctor(null);
75113
75114             tooltip.title = function(val) {
75115                 if (!arguments.length) { return _title; }
75116                 _title = utilFunctor(val);
75117                 return tooltip;
75118             };
75119
75120             tooltip.heading = function(val) {
75121                 if (!arguments.length) { return _heading; }
75122                 _heading = utilFunctor(val);
75123                 return tooltip;
75124             };
75125
75126             tooltip.keys = function(val) {
75127                 if (!arguments.length) { return _keys; }
75128                 _keys = utilFunctor(val);
75129                 return tooltip;
75130             };
75131
75132             tooltip.content(function() {
75133                 var heading = _heading.apply(this, arguments);
75134                 var text = _title.apply(this, arguments);
75135                 var keys = _keys.apply(this, arguments);
75136
75137                 return function(selection) {
75138
75139                     var headingSelect = selection
75140                         .selectAll('.tooltip-heading')
75141                         .data(heading ? [heading] :[]);
75142
75143                     headingSelect.exit()
75144                         .remove();
75145
75146                     headingSelect.enter()
75147                         .append('div')
75148                         .attr('class', 'tooltip-heading')
75149                         .merge(headingSelect)
75150                         .html(heading);
75151
75152                     var textSelect = selection
75153                         .selectAll('.tooltip-text')
75154                         .data(text ? [text] :[]);
75155
75156                     textSelect.exit()
75157                         .remove();
75158
75159                     textSelect.enter()
75160                         .append('div')
75161                         .attr('class', 'tooltip-text')
75162                         .merge(textSelect)
75163                         .html(text);
75164
75165                     var keyhintWrap = selection
75166                         .selectAll('.keyhint-wrap')
75167                         .data(keys && keys.length ? [0] : []);
75168
75169                     keyhintWrap.exit()
75170                         .remove();
75171
75172                     var keyhintWrapEnter = keyhintWrap.enter()
75173                         .append('div')
75174                         .attr('class', 'keyhint-wrap');
75175
75176                     keyhintWrapEnter
75177                         .append('span')
75178                         .html(_t('tooltip_keyhint'));
75179
75180                     keyhintWrap = keyhintWrapEnter.merge(keyhintWrap);
75181
75182                     keyhintWrap.selectAll('kbd.shortcut')
75183                         .data(keys && keys.length ? keys : [])
75184                         .enter()
75185                         .append('kbd')
75186                         .attr('class', 'shortcut')
75187                         .html(function(d) {
75188                             return d;
75189                         });
75190                 };
75191             });
75192
75193             return tooltip;
75194         }
75195
75196         function uiEditMenu(context) {
75197             var dispatch$1 = dispatch('toggled');
75198
75199             var _menu = select(null);
75200             var _operations = [];
75201             // the position the menu should be displayed relative to
75202             var _anchorLoc = [0, 0];
75203             var _anchorLocLonLat = [0, 0];
75204             // a string indicating how the menu was opened
75205             var _triggerType = '';
75206
75207             var _vpTopMargin = 85; // viewport top margin
75208             var _vpBottomMargin = 45; // viewport bottom margin
75209             var _vpSideMargin = 35;   // viewport side margin
75210
75211             var _menuTop = false;
75212             var _menuHeight;
75213             var _menuWidth;
75214
75215             // hardcode these values to make menu positioning easier
75216             var _verticalPadding = 4;
75217
75218             // see also `.edit-menu .tooltip` CSS; include margin
75219             var _tooltipWidth = 210;
75220
75221             // offset the menu slightly from the target location
75222             var _menuSideMargin = 10;
75223
75224             var _tooltips = [];
75225
75226             var editMenu = function(selection) {
75227
75228                 var isTouchMenu = _triggerType.includes('touch') || _triggerType.includes('pen');
75229
75230                 var ops = _operations.filter(function(op) {
75231                     return !isTouchMenu || !op.mouseOnly;
75232                 });
75233
75234                 if (!ops.length) { return; }
75235
75236                 _tooltips = [];
75237
75238                 // Position the menu above the anchor for stylus and finger input
75239                 // since the mapper's hand likely obscures the screen below the anchor
75240                 _menuTop = isTouchMenu;
75241
75242                 // Show labels for touch input since there aren't hover tooltips
75243                 var showLabels = isTouchMenu;
75244
75245                 var buttonHeight = showLabels ? 32 : 34;
75246                 if (showLabels) {
75247                     // Get a general idea of the width based on the length of the label
75248                     _menuWidth = 52 + Math.min(120, 6 * Math.max.apply(Math, ops.map(function(op) {
75249                         return op.title.length;
75250                     })));
75251                 } else {
75252                     _menuWidth = 44;
75253                 }
75254
75255                 _menuHeight = _verticalPadding * 2 + ops.length * buttonHeight;
75256
75257                 _menu = selection
75258                     .append('div')
75259                     .attr('class', 'edit-menu')
75260                     .classed('touch-menu', isTouchMenu)
75261                     .style('padding', _verticalPadding + 'px 0');
75262
75263                 var buttons = _menu.selectAll('.edit-menu-item')
75264                     .data(ops);
75265
75266                 // enter
75267                 var buttonsEnter = buttons.enter()
75268                     .append('button')
75269                     .attr('class', function (d) { return 'edit-menu-item edit-menu-item-' + d.id; })
75270                     .style('height', buttonHeight + 'px')
75271                     .on('click', click)
75272                     // don't listen for `mouseup` because we only care about non-mouse pointer types
75273                     .on('pointerup', pointerup)
75274                     .on('pointerdown mousedown', function pointerdown() {
75275                         // don't let button presses also act as map input - #1869
75276                         event.stopPropagation();
75277                     });
75278
75279                 buttonsEnter.each(function(d) {
75280                     var tooltip = uiTooltip()
75281                         .heading(d.title)
75282                         .title(d.tooltip())
75283                         .keys([d.keys[0]]);
75284
75285                     _tooltips.push(tooltip);
75286
75287                     select(this)
75288                         .call(tooltip)
75289                         .append('div')
75290                         .attr('class', 'icon-wrap')
75291                         .call(svgIcon('#iD-operation-' + d.id, 'operation'));
75292                 });
75293
75294                 if (showLabels) {
75295                     buttonsEnter.append('span')
75296                         .attr('class', 'label')
75297                         .text(function(d) {
75298                             return d.title;
75299                         });
75300                 }
75301
75302                 // update
75303                 buttons = buttonsEnter
75304                     .merge(buttons)
75305                     .classed('disabled', function(d) { return d.disabled(); });
75306
75307                 updatePosition();
75308
75309                 var initialScale = context.projection.scale();
75310                 context.map()
75311                     .on('move.edit-menu', function() {
75312                         if (initialScale !== context.projection.scale()) {
75313                             editMenu.close();
75314                         }
75315                     })
75316                     .on('drawn.edit-menu', function(info) {
75317                         if (info.full) { updatePosition(); }
75318                     });
75319
75320                 var lastPointerUpType;
75321                 // `pointerup` is always called before `click`
75322                 function pointerup() {
75323                     lastPointerUpType = event.pointerType;
75324                 }
75325
75326                 function click(operation) {
75327                     event.stopPropagation();
75328                     if (operation.disabled()) {
75329                         if (lastPointerUpType === 'touch' ||
75330                             lastPointerUpType === 'pen') {
75331                             // there are no tooltips for touch interactions so flash feedback instead
75332                             context.ui().flash
75333                                 .duration(4000)
75334                                 .iconName('#iD-operation-' + operation.id)
75335                                 .iconClass('operation disabled')
75336                                 .text(operation.tooltip)();
75337                         }
75338                     } else {
75339                         if (lastPointerUpType === 'touch' ||
75340                             lastPointerUpType === 'pen') {
75341                             context.ui().flash
75342                                 .duration(2000)
75343                                 .iconName('#iD-operation-' + operation.id)
75344                                 .iconClass('operation')
75345                                 .text(operation.annotation() || operation.title)();
75346                         }
75347
75348                         operation();
75349                         editMenu.close();
75350                     }
75351                     lastPointerUpType = null;
75352                 }
75353
75354                 dispatch$1.call('toggled', this, true);
75355             };
75356
75357             function updatePosition() {
75358
75359                 if (!_menu || _menu.empty()) { return; }
75360
75361                 var anchorLoc = context.projection(_anchorLocLonLat);
75362
75363                 var viewport = context.surfaceRect();
75364
75365                 if (anchorLoc[0] < 0 ||
75366                     anchorLoc[0] > viewport.width ||
75367                     anchorLoc[1] < 0 ||
75368                     anchorLoc[1] > viewport.height) {
75369                     // close the menu if it's gone offscreen
75370
75371                     editMenu.close();
75372                     return;
75373                 }
75374
75375                 var menuLeft = displayOnLeft(viewport);
75376
75377                 var offset = [0, 0];
75378
75379                 offset[0] = menuLeft ? -1 * (_menuSideMargin + _menuWidth) : _menuSideMargin;
75380
75381                 if (_menuTop) {
75382                     if (anchorLoc[1] - _menuHeight < _vpTopMargin) {
75383                         // menu is near top viewport edge, shift downward
75384                         offset[1] = -anchorLoc[1] + _vpTopMargin;
75385                     } else {
75386                         offset[1] = -_menuHeight;
75387                     }
75388                 } else {
75389                     if (anchorLoc[1] + _menuHeight > (viewport.height - _vpBottomMargin)) {
75390                         // menu is near bottom viewport edge, shift upwards
75391                         offset[1] = -anchorLoc[1] - _menuHeight + viewport.height - _vpBottomMargin;
75392                     } else {
75393                         offset[1] = 0;
75394                     }
75395                 }
75396
75397                 var origin = geoVecAdd(anchorLoc, offset);
75398
75399                 _menu
75400                     .style('left', origin[0] + 'px')
75401                     .style('top', origin[1] + 'px');
75402
75403                 var tooltipSide = tooltipPosition(viewport, menuLeft);
75404                 _tooltips.forEach(function(tooltip) {
75405                     tooltip.placement(tooltipSide);
75406                 });
75407
75408                 function displayOnLeft(viewport) {
75409                     if (_mainLocalizer.textDirection() === 'ltr') {
75410                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth) > (viewport.width - _vpSideMargin)) {
75411                             // right menu would be too close to the right viewport edge, go left
75412                             return true;
75413                         }
75414                         // prefer right menu
75415                         return false;
75416
75417                     } else { // rtl
75418                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth) < _vpSideMargin) {
75419                             // left menu would be too close to the left viewport edge, go right
75420                             return false;
75421                         }
75422                         // prefer left menu
75423                         return true;
75424                     }
75425                 }
75426
75427                 function tooltipPosition(viewport, menuLeft) {
75428                     if (_mainLocalizer.textDirection() === 'ltr') {
75429                         if (menuLeft) {
75430                             // if there's not room for a right-side menu then there definitely
75431                             // isn't room for right-side tooltips
75432                             return 'left';
75433                         }
75434                         if ((anchorLoc[0] + _menuSideMargin + _menuWidth + _tooltipWidth) > (viewport.width - _vpSideMargin)) {
75435                             // right tooltips would be too close to the right viewport edge, go left
75436                             return 'left';
75437                         }
75438                         // prefer right tooltips
75439                         return 'right';
75440
75441                     } else { // rtl
75442                         if (!menuLeft) {
75443                             return 'right';
75444                         }
75445                         if ((anchorLoc[0] - _menuSideMargin - _menuWidth - _tooltipWidth) < _vpSideMargin) {
75446                             // left tooltips would be too close to the left viewport edge, go right
75447                             return 'right';
75448                         }
75449                         // prefer left tooltips
75450                         return 'left';
75451                     }
75452                 }
75453             }
75454
75455             editMenu.close = function () {
75456
75457                 context.map()
75458                     .on('move.edit-menu', null)
75459                     .on('drawn.edit-menu', null);
75460
75461                 _menu.remove();
75462                 _tooltips = [];
75463
75464                 dispatch$1.call('toggled', this, false);
75465             };
75466
75467             editMenu.anchorLoc = function(val) {
75468                 if (!arguments.length) { return _anchorLoc; }
75469                 _anchorLoc = val;
75470                 _anchorLocLonLat = context.projection.invert(_anchorLoc);
75471                 return editMenu;
75472             };
75473
75474             editMenu.triggerType = function(val) {
75475                 if (!arguments.length) { return _triggerType; }
75476                 _triggerType = val;
75477                 return editMenu;
75478             };
75479
75480             editMenu.operations = function(val) {
75481                 if (!arguments.length) { return _operations; }
75482                 _operations = val;
75483                 return editMenu;
75484             };
75485
75486             return utilRebind(editMenu, dispatch$1, 'on');
75487         }
75488
75489         function uiFeatureInfo(context) {
75490             function update(selection) {
75491                 var features = context.features();
75492                 var stats = features.stats();
75493                 var count = 0;
75494                 var hiddenList = features.hidden().map(function(k) {
75495                     if (stats[k]) {
75496                         count += stats[k];
75497                         return String(stats[k]) + ' ' + _t('feature.' + k + '.description');
75498                     }
75499                 }).filter(Boolean);
75500
75501                 selection.html('');
75502
75503                 if (hiddenList.length) {
75504                     var tooltipBehavior = uiTooltip()
75505                         .placement('top')
75506                         .title(function() {
75507                             return hiddenList.join('<br/>');
75508                         });
75509
75510                     selection.append('a')
75511                         .attr('class', 'chip')
75512                         .attr('href', '#')
75513                         .attr('tabindex', -1)
75514                         .html(_t('feature_info.hidden_warning', { count: count }))
75515                         .call(tooltipBehavior)
75516                         .on('click', function() {
75517                             tooltipBehavior.hide();
75518                             event.preventDefault();
75519                             // open the Map Data pane
75520                             context.ui().togglePanes(context.container().select('.map-panes .map-data-pane'));
75521                         });
75522                 }
75523
75524                 selection
75525                     .classed('hide', !hiddenList.length);
75526             }
75527
75528
75529             return function(selection) {
75530                 update(selection);
75531
75532                 context.features().on('change.feature_info', function() {
75533                     update(selection);
75534                 });
75535             };
75536         }
75537
75538         function uiFlash(context) {
75539             var _flashTimer;
75540
75541             var _duration = 2000;
75542             var _iconName = '#iD-icon-no';
75543             var _iconClass = 'disabled';
75544             var _text = '';
75545             var _textClass;
75546
75547             function flash() {
75548                 if (_flashTimer) {
75549                     _flashTimer.stop();
75550                 }
75551
75552                 context.container().select('.main-footer-wrap')
75553                     .classed('footer-hide', true)
75554                     .classed('footer-show', false);
75555                 context.container().select('.flash-wrap')
75556                     .classed('footer-hide', false)
75557                     .classed('footer-show', true);
75558
75559                 var content = context.container().select('.flash-wrap').selectAll('.flash-content')
75560                     .data([0]);
75561
75562                 // Enter
75563                 var contentEnter = content.enter()
75564                     .append('div')
75565                     .attr('class', 'flash-content');
75566
75567                 var iconEnter = contentEnter
75568                     .append('svg')
75569                     .attr('class', 'flash-icon icon')
75570                     .append('g')
75571                     .attr('transform', 'translate(10,10)');
75572
75573                 iconEnter
75574                     .append('circle')
75575                     .attr('r', 9);
75576
75577                 iconEnter
75578                     .append('use')
75579                     .attr('transform', 'translate(-7,-7)')
75580                     .attr('width', '14')
75581                     .attr('height', '14');
75582
75583                 contentEnter
75584                     .append('div')
75585                     .attr('class', 'flash-text');
75586
75587
75588                 // Update
75589                 content = content
75590                     .merge(contentEnter);
75591
75592                 content
75593                     .selectAll('.flash-icon')
75594                     .attr('class', 'icon flash-icon ' + (_iconClass || ''));
75595
75596                 content
75597                     .selectAll('.flash-icon use')
75598                     .attr('xlink:href', _iconName);
75599
75600                 content
75601                     .selectAll('.flash-text')
75602                     .attr('class', 'flash-text ' + (_textClass || ''))
75603                     .text(_text);
75604
75605
75606                 _flashTimer = d3_timeout(function() {
75607                     _flashTimer = null;
75608                     context.container().select('.main-footer-wrap')
75609                         .classed('footer-hide', false)
75610                         .classed('footer-show', true);
75611                     context.container().select('.flash-wrap')
75612                         .classed('footer-hide', true)
75613                         .classed('footer-show', false);
75614                 }, _duration);
75615
75616                 return content;
75617             }
75618
75619
75620             flash.duration = function(_) {
75621                 if (!arguments.length) { return _duration; }
75622                 _duration = _;
75623                 return flash;
75624             };
75625
75626             flash.text = function(_) {
75627                 if (!arguments.length) { return _text; }
75628                 _text = _;
75629                 return flash;
75630             };
75631
75632             flash.textClass = function(_) {
75633                 if (!arguments.length) { return _textClass; }
75634                 _textClass = _;
75635                 return flash;
75636             };
75637
75638             flash.iconName = function(_) {
75639                 if (!arguments.length) { return _iconName; }
75640                 _iconName = _;
75641                 return flash;
75642             };
75643
75644             flash.iconClass = function(_) {
75645                 if (!arguments.length) { return _iconClass; }
75646                 _iconClass = _;
75647                 return flash;
75648             };
75649
75650             return flash;
75651         }
75652
75653         function uiFullScreen(context) {
75654             var element = context.container().node();
75655             // var button = d3_select(null);
75656
75657
75658             function getFullScreenFn() {
75659                 if (element.requestFullscreen) {
75660                     return element.requestFullscreen;
75661                 } else if (element.msRequestFullscreen) {
75662                     return element.msRequestFullscreen;
75663                 } else if (element.mozRequestFullScreen) {
75664                     return element.mozRequestFullScreen;
75665                 } else if (element.webkitRequestFullscreen) {
75666                     return element.webkitRequestFullscreen;
75667                 }
75668             }
75669
75670
75671             function getExitFullScreenFn() {
75672                 if (document.exitFullscreen) {
75673                     return document.exitFullscreen;
75674                 } else if (document.msExitFullscreen) {
75675                     return document.msExitFullscreen;
75676                 } else if (document.mozCancelFullScreen) {
75677                     return document.mozCancelFullScreen;
75678                 } else if (document.webkitExitFullscreen) {
75679                     return document.webkitExitFullscreen;
75680                 }
75681             }
75682
75683
75684             function isFullScreen() {
75685                 return document.fullscreenElement ||
75686                     document.mozFullScreenElement ||
75687                     document.webkitFullscreenElement ||
75688                     document.msFullscreenElement;
75689             }
75690
75691
75692             function isSupported() {
75693                 return !!getFullScreenFn();
75694             }
75695
75696
75697             function fullScreen() {
75698                 event.preventDefault();
75699                 if (!isFullScreen()) {
75700                     // button.classed('active', true);
75701                     getFullScreenFn().apply(element);
75702                 } else {
75703                     // button.classed('active', false);
75704                     getExitFullScreenFn().apply(document);
75705                 }
75706             }
75707
75708
75709             return function() { // selection) {
75710                 if (!isSupported()) { return; }
75711
75712                 // button = selection.append('button')
75713                 //     .attr('title', t('full_screen'))
75714                 //     .attr('tabindex', -1)
75715                 //     .on('click', fullScreen)
75716                 //     .call(tooltip);
75717
75718                 // button.append('span')
75719                 //     .attr('class', 'icon full-screen');
75720
75721                 var detected = utilDetect();
75722                 var keys = (detected.os === 'mac' ? [uiCmd('⌃⌘F'), 'f11'] : ['f11']);
75723                 context.keybinding().on(keys, fullScreen);
75724             };
75725         }
75726
75727         function uiGeolocate(context) {
75728             var _geolocationOptions = {
75729                 // prioritize speed and power usage over precision
75730                 enableHighAccuracy: false,
75731                 // don't hang indefinitely getting the location
75732                 timeout: 6000 // 6sec
75733             };
75734             var _locating = uiLoading(context).message(_t('geolocate.locating')).blocking(true);
75735             var _layer = context.layers().layer('geolocate');
75736             var _position;
75737             var _extent;
75738             var _timeoutID;
75739             var _button = select(null);
75740
75741             function click() {
75742                 if (context.inIntro()) { return; }
75743                 if (!_layer.enabled() && !_locating.isShown()) {
75744
75745                     // This timeout ensures that we still call finish() even if
75746                     // the user declines to share their location in Firefox
75747                     _timeoutID = setTimeout(error, 10000 /* 10sec */ );
75748
75749                     context.container().call(_locating);
75750                     // get the latest position even if we already have one
75751                     navigator.geolocation.getCurrentPosition(success, error, _geolocationOptions);
75752                 } else {
75753                     _locating.close();
75754                     _layer.enabled(null, false);
75755                     updateButtonState();
75756                 }
75757             }
75758
75759             function zoomTo() {
75760                 context.enter(modeBrowse(context));
75761
75762                 var map = context.map();
75763                 _layer.enabled(_position, true);
75764                 updateButtonState();
75765                 map.centerZoomEase(_extent.center(), Math.min(20, map.extentZoom(_extent)));
75766             }
75767
75768             function success(geolocation) {
75769                 _position = geolocation;
75770                 var coords = _position.coords;
75771                 _extent = geoExtent([coords.longitude, coords.latitude]).padByMeters(coords.accuracy);
75772                 zoomTo();
75773                 finish();
75774             }
75775
75776             function error() {
75777                 if (_position) {
75778                     // use the position from a previous call if we have one
75779                     zoomTo();
75780                 } else {
75781                     context.ui().flash
75782                         .text(_t('geolocate.location_unavailable'))
75783                         .iconName('#iD-icon-geolocate')();
75784                 }
75785
75786                 finish();
75787             }
75788
75789             function finish() {
75790                 _locating.close();  // unblock ui
75791                 if (_timeoutID) { clearTimeout(_timeoutID); }
75792                 _timeoutID = undefined;
75793             }
75794
75795             function updateButtonState() {
75796                 _button.classed('active', _layer.enabled());
75797             }
75798
75799             return function(selection) {
75800                 if (!navigator.geolocation || !navigator.geolocation.getCurrentPosition) { return; }
75801
75802                 _button = selection
75803                     .append('button')
75804                     .on('click', click)
75805                     .call(svgIcon('#iD-icon-geolocate', 'light'))
75806                     .call(uiTooltip()
75807                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
75808                         .title(_t('geolocate.title'))
75809                         .keys([_t('geolocate.key')])
75810                     );
75811
75812                 context.keybinding().on(_t('geolocate.key'), click);
75813             };
75814         }
75815
75816         function uiPanelBackground(context) {
75817             var background = context.background();
75818             var currSourceName = null;
75819             var metadata = {};
75820             var metadataKeys = [
75821                 'zoom', 'vintage', 'source', 'description', 'resolution', 'accuracy'
75822             ];
75823
75824             var debouncedRedraw = debounce(redraw, 250);
75825
75826             function redraw(selection) {
75827                 var source = background.baseLayerSource();
75828                 if (!source) { return; }
75829
75830                 var isDG = (source.id.match(/^DigitalGlobe/i) !== null);
75831
75832                 if (currSourceName !== source.name()) {
75833                     currSourceName = source.name();
75834                     metadata = {};
75835                 }
75836
75837                 selection.html('');
75838
75839                 var list = selection
75840                     .append('ul')
75841                     .attr('class', 'background-info');
75842
75843                 list
75844                     .append('li')
75845                     .text(currSourceName);
75846
75847                 metadataKeys.forEach(function(k) {
75848                     // DigitalGlobe vintage is available in raster layers for now.
75849                     if (isDG && k === 'vintage') { return; }
75850
75851                     list
75852                         .append('li')
75853                         .attr('class', 'background-info-list-' + k)
75854                         .classed('hide', !metadata[k])
75855                         .text(_t('info_panels.background.' + k) + ':')
75856                         .append('span')
75857                         .attr('class', 'background-info-span-' + k)
75858                         .text(metadata[k]);
75859                 });
75860
75861                 debouncedGetMetadata(selection);
75862
75863                 var toggleTiles = context.getDebug('tile') ? 'hide_tiles' : 'show_tiles';
75864
75865                 selection
75866                     .append('a')
75867                     .text(_t('info_panels.background.' + toggleTiles))
75868                     .attr('href', '#')
75869                     .attr('class', 'button button-toggle-tiles')
75870                     .on('click', function() {
75871                         event.preventDefault();
75872                         context.setDebug('tile', !context.getDebug('tile'));
75873                         selection.call(redraw);
75874                     });
75875
75876                 if (isDG) {
75877                     var key = source.id + '-vintage';
75878                     var sourceVintage = context.background().findSource(key);
75879                     var showsVintage = context.background().showsLayer(sourceVintage);
75880                     var toggleVintage = showsVintage ? 'hide_vintage' : 'show_vintage';
75881                     selection
75882                         .append('a')
75883                         .text(_t('info_panels.background.' + toggleVintage))
75884                         .attr('href', '#')
75885                         .attr('class', 'button button-toggle-vintage')
75886                         .on('click', function() {
75887                             event.preventDefault();
75888                             context.background().toggleOverlayLayer(sourceVintage);
75889                             selection.call(redraw);
75890                         });
75891                 }
75892
75893                 // disable if necessary
75894                 ['DigitalGlobe-Premium', 'DigitalGlobe-Standard'].forEach(function(layerId) {
75895                     if (source.id !== layerId) {
75896                         var key = layerId + '-vintage';
75897                         var sourceVintage = context.background().findSource(key);
75898                         if (context.background().showsLayer(sourceVintage)) {
75899                             context.background().toggleOverlayLayer(sourceVintage);
75900                         }
75901                     }
75902                 });
75903             }
75904
75905
75906             var debouncedGetMetadata = debounce(getMetadata, 250);
75907
75908             function getMetadata(selection) {
75909                 var tile = context.container().select('.layer-background img.tile-center');   // tile near viewport center
75910                 if (tile.empty()) { return; }
75911
75912                 var sourceName = currSourceName;
75913                 var d = tile.datum();
75914                 var zoom = (d && d.length >= 3 && d[2]) || Math.floor(context.map().zoom());
75915                 var center = context.map().center();
75916
75917                 // update zoom
75918                 metadata.zoom = String(zoom);
75919                 selection.selectAll('.background-info-list-zoom')
75920                     .classed('hide', false)
75921                     .selectAll('.background-info-span-zoom')
75922                     .text(metadata.zoom);
75923
75924                 if (!d || !d.length >= 3) { return; }
75925
75926                 background.baseLayerSource().getMetadata(center, d, function(err, result) {
75927                     if (err || currSourceName !== sourceName) { return; }
75928
75929                     // update vintage
75930                     var vintage = result.vintage;
75931                     metadata.vintage = (vintage && vintage.range) || _t('info_panels.background.unknown');
75932                     selection.selectAll('.background-info-list-vintage')
75933                         .classed('hide', false)
75934                         .selectAll('.background-info-span-vintage')
75935                         .text(metadata.vintage);
75936
75937                     // update other metdata
75938                     metadataKeys.forEach(function(k) {
75939                         if (k === 'zoom' || k === 'vintage') { return; }  // done already
75940                         var val = result[k];
75941                         metadata[k] = val;
75942                         selection.selectAll('.background-info-list-' + k)
75943                             .classed('hide', !val)
75944                             .selectAll('.background-info-span-' + k)
75945                             .text(val);
75946                     });
75947                 });
75948             }
75949
75950
75951             var panel = function(selection) {
75952                 selection.call(redraw);
75953
75954                 context.map()
75955                     .on('drawn.info-background', function() {
75956                         selection.call(debouncedRedraw);
75957                     })
75958                     .on('move.info-background', function() {
75959                         selection.call(debouncedGetMetadata);
75960                     });
75961
75962             };
75963
75964             panel.off = function() {
75965                 context.map()
75966                     .on('drawn.info-background', null)
75967                     .on('move.info-background', null);
75968             };
75969
75970             panel.id = 'background';
75971             panel.title = _t('info_panels.background.title');
75972             panel.key = _t('info_panels.background.key');
75973
75974
75975             return panel;
75976         }
75977
75978         function uiPanelHistory(context) {
75979             var osm;
75980
75981             function displayTimestamp(timestamp) {
75982                 if (!timestamp) { return _t('info_panels.history.unknown'); }
75983                 var options = {
75984                     day: 'numeric', month: 'short', year: 'numeric',
75985                     hour: 'numeric', minute: 'numeric', second: 'numeric'
75986                 };
75987                 var d = new Date(timestamp);
75988                 if (isNaN(d.getTime())) { return _t('info_panels.history.unknown'); }
75989                 return d.toLocaleString(_mainLocalizer.localeCode(), options);
75990             }
75991
75992
75993             function displayUser(selection, userName) {
75994                 if (!userName) {
75995                     selection
75996                         .append('span')
75997                         .text(_t('info_panels.history.unknown'));
75998                     return;
75999                 }
76000
76001                 selection
76002                     .append('span')
76003                     .attr('class', 'user-name')
76004                     .text(userName);
76005
76006                 var links = selection
76007                     .append('div')
76008                     .attr('class', 'links');
76009
76010                 if (osm) {
76011                     links
76012                         .append('a')
76013                         .attr('class', 'user-osm-link')
76014                         .attr('href', osm.userURL(userName))
76015                         .attr('target', '_blank')
76016                         .attr('tabindex', -1)
76017                         .text('OSM');
76018                 }
76019
76020                 links
76021                     .append('a')
76022                     .attr('class', 'user-hdyc-link')
76023                     .attr('href', 'https://hdyc.neis-one.org/?' + userName)
76024                     .attr('target', '_blank')
76025                     .attr('tabindex', -1)
76026                     .text('HDYC');
76027             }
76028
76029
76030             function displayChangeset(selection, changeset) {
76031                 if (!changeset) {
76032                     selection
76033                         .append('span')
76034                         .text(_t('info_panels.history.unknown'));
76035                     return;
76036                 }
76037
76038                 selection
76039                     .append('span')
76040                     .attr('class', 'changeset-id')
76041                     .text(changeset);
76042
76043                 var links = selection
76044                     .append('div')
76045                     .attr('class', 'links');
76046
76047                 if (osm) {
76048                     links
76049                         .append('a')
76050                         .attr('class', 'changeset-osm-link')
76051                         .attr('href', osm.changesetURL(changeset))
76052                         .attr('target', '_blank')
76053                         .attr('tabindex', -1)
76054                         .text('OSM');
76055                 }
76056
76057                 links
76058                     .append('a')
76059                     .attr('class', 'changeset-osmcha-link')
76060                     .attr('href', 'https://osmcha.org/changesets/' + changeset)
76061                     .attr('target', '_blank')
76062                     .attr('tabindex', -1)
76063                     .text('OSMCha');
76064
76065                 links
76066                     .append('a')
76067                     .attr('class', 'changeset-achavi-link')
76068                     .attr('href', 'https://overpass-api.de/achavi/?changeset=' + changeset)
76069                     .attr('target', '_blank')
76070                     .attr('tabindex', -1)
76071                     .text('Achavi');
76072             }
76073
76074
76075             function redraw(selection) {
76076                 var selectedNoteID = context.selectedNoteID();
76077                 osm = context.connection();
76078
76079                 var selected, note, entity;
76080                 if (selectedNoteID && osm) {       // selected 1 note
76081                     selected = [ _t('note.note') + ' ' + selectedNoteID ];
76082                     note = osm.getNote(selectedNoteID);
76083                 } else {                           // selected 1..n entities
76084                     selected = context.selectedIDs()
76085                         .filter(function(e) { return context.hasEntity(e); });
76086                     if (selected.length) {
76087                         entity = context.entity(selected[0]);
76088                     }
76089                 }
76090
76091                 var singular = selected.length === 1 ? selected[0] : null;
76092
76093                 selection.html('');
76094
76095                 selection
76096                     .append('h4')
76097                     .attr('class', 'history-heading')
76098                     .text(singular || _t('info_panels.history.selected', { n: selected.length }));
76099
76100                 if (!singular) { return; }
76101
76102                 if (entity) {
76103                     selection.call(redrawEntity, entity);
76104                 } else if (note) {
76105                     selection.call(redrawNote, note);
76106                 }
76107             }
76108
76109
76110             function redrawNote(selection, note) {
76111                 if (!note || note.isNew()) {
76112                     selection
76113                         .append('div')
76114                         .text(_t('info_panels.history.note_no_history'));
76115                     return;
76116                 }
76117
76118                 var list = selection
76119                     .append('ul');
76120
76121                 list
76122                     .append('li')
76123                     .text(_t('info_panels.history.note_comments') + ':')
76124                     .append('span')
76125                     .text(note.comments.length);
76126
76127                 if (note.comments.length) {
76128                     list
76129                         .append('li')
76130                         .text(_t('info_panels.history.note_created_date') + ':')
76131                         .append('span')
76132                         .text(displayTimestamp(note.comments[0].date));
76133
76134                     list
76135                         .append('li')
76136                         .text(_t('info_panels.history.note_created_user') + ':')
76137                         .call(displayUser, note.comments[0].user);
76138                 }
76139
76140                 if (osm) {
76141                     selection
76142                         .append('a')
76143                         .attr('class', 'view-history-on-osm')
76144                         .attr('target', '_blank')
76145                         .attr('tabindex', -1)
76146                         .attr('href', osm.noteURL(note))
76147                         .call(svgIcon('#iD-icon-out-link', 'inline'))
76148                         .append('span')
76149                         .text(_t('info_panels.history.note_link_text'));
76150                 }
76151             }
76152
76153
76154             function redrawEntity(selection, entity) {
76155                 if (!entity || entity.isNew()) {
76156                     selection
76157                         .append('div')
76158                         .text(_t('info_panels.history.no_history'));
76159                     return;
76160                 }
76161
76162                 var links = selection
76163                     .append('div')
76164                     .attr('class', 'links');
76165
76166                 if (osm) {
76167                     links
76168                         .append('a')
76169                         .attr('class', 'view-history-on-osm')
76170                         .attr('href', osm.historyURL(entity))
76171                         .attr('target', '_blank')
76172                         .attr('tabindex', -1)
76173                         .attr('title', _t('info_panels.history.link_text'))
76174                         .text('OSM');
76175                 }
76176                 links
76177                     .append('a')
76178                     .attr('class', 'pewu-history-viewer-link')
76179                     .attr('href', 'https://pewu.github.io/osm-history/#/' + entity.type + '/' + entity.osmId())
76180                     .attr('target', '_blank')
76181                     .attr('tabindex', -1)
76182                     .text('PeWu');
76183
76184                 var list = selection
76185                     .append('ul');
76186
76187                 list
76188                     .append('li')
76189                     .text(_t('info_panels.history.version') + ':')
76190                     .append('span')
76191                     .text(entity.version);
76192
76193                 list
76194                     .append('li')
76195                     .text(_t('info_panels.history.last_edit') + ':')
76196                     .append('span')
76197                     .text(displayTimestamp(entity.timestamp));
76198
76199                 list
76200                     .append('li')
76201                     .text(_t('info_panels.history.edited_by') + ':')
76202                     .call(displayUser, entity.user);
76203
76204                 list
76205                     .append('li')
76206                     .text(_t('info_panels.history.changeset') + ':')
76207                     .call(displayChangeset, entity.changeset);
76208             }
76209
76210
76211             var panel = function(selection) {
76212                 selection.call(redraw);
76213
76214                 context.map()
76215                     .on('drawn.info-history', function() {
76216                         selection.call(redraw);
76217                     });
76218
76219                 context
76220                     .on('enter.info-history', function() {
76221                         selection.call(redraw);
76222                     });
76223             };
76224
76225             panel.off = function() {
76226                 context.map().on('drawn.info-history', null);
76227                 context.on('enter.info-history', null);
76228             };
76229
76230             panel.id = 'history';
76231             panel.title = _t('info_panels.history.title');
76232             panel.key = _t('info_panels.history.key');
76233
76234
76235             return panel;
76236         }
76237
76238         var OSM_PRECISION = 7;
76239
76240         /**
76241          * Returns a localized representation of the given length measurement.
76242          *
76243          * @param {Number} m area in meters
76244          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76245          */
76246         function displayLength(m, isImperial) {
76247             var d = m * (isImperial ? 3.28084 : 1);
76248             var unit;
76249
76250             if (isImperial) {
76251                 if (d >= 5280) {
76252                     d /= 5280;
76253                     unit = 'miles';
76254                 } else {
76255                     unit = 'feet';
76256                 }
76257             } else {
76258                 if (d >= 1000) {
76259                     d /= 1000;
76260                     unit = 'kilometers';
76261                 } else {
76262                     unit = 'meters';
76263                 }
76264             }
76265
76266             return _t('units.' + unit, {
76267                 quantity: d.toLocaleString(_mainLocalizer.localeCode(), {
76268                     maximumSignificantDigits: 4
76269                 })
76270             });
76271         }
76272
76273         /**
76274          * Returns a localized representation of the given area measurement.
76275          *
76276          * @param {Number} m2 area in square meters
76277          * @param {Boolean} isImperial true for U.S. customary units; false for metric
76278          */
76279         function displayArea(m2, isImperial) {
76280             var locale = _mainLocalizer.localeCode();
76281             var d = m2 * (isImperial ? 10.7639111056 : 1);
76282             var d1, d2, area;
76283             var unit1 = '';
76284             var unit2 = '';
76285
76286             if (isImperial) {
76287                 if (d >= 6969600) { // > 0.25mi² show mi²
76288                     d1 = d / 27878400;
76289                     unit1 = 'square_miles';
76290                 } else {
76291                     d1 = d;
76292                     unit1 = 'square_feet';
76293                 }
76294
76295                 if (d > 4356 && d < 43560000) { // 0.1 - 1000 acres
76296                     d2 = d / 43560;
76297                     unit2 = 'acres';
76298                 }
76299
76300             } else {
76301                 if (d >= 250000) { // > 0.25km² show km²
76302                     d1 = d / 1000000;
76303                     unit1 = 'square_kilometers';
76304                 } else {
76305                     d1 = d;
76306                     unit1 = 'square_meters';
76307                 }
76308
76309                 if (d > 1000 && d < 10000000) { // 0.1 - 1000 hectares
76310                     d2 = d / 10000;
76311                     unit2 = 'hectares';
76312                 }
76313             }
76314
76315             area = _t('units.' + unit1, {
76316                 quantity: d1.toLocaleString(locale, {
76317                     maximumSignificantDigits: 4
76318                 })
76319             });
76320
76321             if (d2) {
76322                 return _t('units.area_pair', {
76323                     area1: area,
76324                     area2: _t('units.' + unit2, {
76325                         quantity: d2.toLocaleString(locale, {
76326                             maximumSignificantDigits: 2
76327                         })
76328                     })
76329                 });
76330             } else {
76331                 return area;
76332             }
76333         }
76334
76335         function wrap(x, min, max) {
76336             var d = max - min;
76337             return ((x - min) % d + d) % d + min;
76338         }
76339
76340         function clamp$1(x, min, max) {
76341             return Math.max(min, Math.min(x, max));
76342         }
76343
76344         function displayCoordinate(deg, pos, neg) {
76345             var locale = _mainLocalizer.localeCode();
76346             var min = (Math.abs(deg) - Math.floor(Math.abs(deg))) * 60;
76347             var sec = (min - Math.floor(min)) * 60;
76348             var displayDegrees = _t('units.arcdegrees', {
76349                 quantity: Math.floor(Math.abs(deg)).toLocaleString(locale)
76350             });
76351             var displayCoordinate;
76352
76353             if (Math.floor(sec) > 0) {
76354                 displayCoordinate = displayDegrees +
76355                     _t('units.arcminutes', {
76356                         quantity: Math.floor(min).toLocaleString(locale)
76357                     }) +
76358                     _t('units.arcseconds', {
76359                         quantity: Math.round(sec).toLocaleString(locale)
76360                     });
76361             } else if (Math.floor(min) > 0) {
76362                 displayCoordinate = displayDegrees +
76363                     _t('units.arcminutes', {
76364                         quantity: Math.round(min).toLocaleString(locale)
76365                     });
76366             } else {
76367                 displayCoordinate = _t('units.arcdegrees', {
76368                     quantity: Math.round(Math.abs(deg)).toLocaleString(locale)
76369                 });
76370             }
76371
76372             if (deg === 0) {
76373                 return displayCoordinate;
76374             } else {
76375                 return _t('units.coordinate', {
76376                     coordinate: displayCoordinate,
76377                     direction: _t('units.' + (deg > 0 ? pos : neg))
76378                 });
76379             }
76380         }
76381
76382         /**
76383          * Returns given coordinate pair in degree-minute-second format.
76384          *
76385          * @param {Array<Number>} coord longitude and latitude
76386          */
76387         function dmsCoordinatePair(coord) {
76388             return _t('units.coordinate_pair', {
76389                 latitude: displayCoordinate(clamp$1(coord[1], -90, 90), 'north', 'south'),
76390                 longitude: displayCoordinate(wrap(coord[0], -180, 180), 'east', 'west')
76391             });
76392         }
76393
76394         /**
76395          * Returns the given coordinate pair in decimal format.
76396          * note: unlocalized to avoid comma ambiguity - see #4765
76397          *
76398          * @param {Array<Number>} coord longitude and latitude
76399          */
76400         function decimalCoordinatePair(coord) {
76401             return _t('units.coordinate_pair', {
76402                 latitude: clamp$1(coord[1], -90, 90).toFixed(OSM_PRECISION),
76403                 longitude: wrap(coord[0], -180, 180).toFixed(OSM_PRECISION)
76404             });
76405         }
76406
76407         function uiPanelLocation(context) {
76408             var currLocation = '';
76409
76410
76411             function redraw(selection) {
76412                 selection.html('');
76413
76414                 var list = selection
76415                     .append('ul');
76416
76417                 // Mouse coordinates
76418                 var coord = context.map().mouseCoordinates();
76419                 if (coord.some(isNaN)) {
76420                     coord = context.map().center();
76421                 }
76422
76423                 list
76424                     .append('li')
76425                     .text(dmsCoordinatePair(coord))
76426                     .append('li')
76427                     .text(decimalCoordinatePair(coord));
76428
76429                 // Location Info
76430                 selection
76431                     .append('div')
76432                     .attr('class', 'location-info')
76433                     .text(currLocation || ' ');
76434
76435                 debouncedGetLocation(selection, coord);
76436             }
76437
76438
76439             var debouncedGetLocation = debounce(getLocation, 250);
76440             function getLocation(selection, coord) {
76441                 if (!services.geocoder) {
76442                     currLocation = _t('info_panels.location.unknown_location');
76443                     selection.selectAll('.location-info')
76444                         .text(currLocation);
76445                 } else {
76446                     services.geocoder.reverse(coord, function(err, result) {
76447                         currLocation = result ? result.display_name : _t('info_panels.location.unknown_location');
76448                         selection.selectAll('.location-info')
76449                             .text(currLocation);
76450                     });
76451                 }
76452             }
76453
76454
76455             var panel = function(selection) {
76456                 selection.call(redraw);
76457
76458                 context.surface()
76459                     .on(('PointerEvent' in window ? 'pointer' : 'mouse') + 'move.info-location', function() {
76460                         selection.call(redraw);
76461                     });
76462             };
76463
76464             panel.off = function() {
76465                 context.surface()
76466                     .on('.info-location', null);
76467             };
76468
76469             panel.id = 'location';
76470             panel.title = _t('info_panels.location.title');
76471             panel.key = _t('info_panels.location.key');
76472
76473
76474             return panel;
76475         }
76476
76477         function uiPanelMeasurement(context) {
76478             var locale = _mainLocalizer.localeCode();
76479             var isImperial = !_mainLocalizer.usesMetric();
76480
76481
76482             function radiansToMeters(r) {
76483                 // using WGS84 authalic radius (6371007.1809 m)
76484                 return r * 6371007.1809;
76485             }
76486
76487             function steradiansToSqmeters(r) {
76488                 // http://gis.stackexchange.com/a/124857/40446
76489                 return r / (4 * Math.PI) * 510065621724000;
76490             }
76491
76492
76493             function toLineString(feature) {
76494                 if (feature.type === 'LineString') { return feature; }
76495
76496                 var result = { type: 'LineString', coordinates: [] };
76497                 if (feature.type === 'Polygon') {
76498                     result.coordinates = feature.coordinates[0];
76499                 } else if (feature.type === 'MultiPolygon') {
76500                     result.coordinates = feature.coordinates[0][0];
76501                 }
76502
76503                 return result;
76504             }
76505
76506
76507             function redraw(selection) {
76508                 var graph = context.graph();
76509                 var selectedNoteID = context.selectedNoteID();
76510                 var osm = services.osm;
76511
76512                 var heading;
76513                 var center, location, centroid;
76514                 var closed, geometry;
76515                 var totalNodeCount, length = 0, area = 0;
76516
76517                 if (selectedNoteID && osm) {       // selected 1 note
76518
76519                     var note = osm.getNote(selectedNoteID);
76520                     heading = _t('note.note') + ' ' + selectedNoteID;
76521                     location = note.loc;
76522                     geometry = 'note';
76523
76524                 } else {                           // selected 1..n entities
76525                     var selectedIDs = context.selectedIDs().filter(function(id) {
76526                         return context.hasEntity(id);
76527                     });
76528                     var selected = selectedIDs.map(function(id) {
76529                         return context.entity(id);
76530                     });
76531
76532                     heading = selected.length === 1 ? selected[0].id :
76533                         _t('info_panels.measurement.selected', { n: selected.length.toLocaleString(locale) });
76534
76535                     if (selected.length) {
76536                         var extent = geoExtent();
76537                         for (var i in selected) {
76538                             var entity = selected[i];
76539                             extent._extend(entity.extent(graph));
76540
76541                             geometry = entity.geometry(graph);
76542                             if (geometry === 'line' || geometry === 'area') {
76543                                 closed = (entity.type === 'relation') || (entity.isClosed() && !entity.isDegenerate());
76544                                 var feature = entity.asGeoJSON(graph);
76545                                 length += radiansToMeters(d3_geoLength(toLineString(feature)));
76546                                 centroid = d3_geoCentroid(feature);
76547                                 if (closed) {
76548                                     area += steradiansToSqmeters(entity.area(graph));
76549                                 }
76550                             }
76551                         }
76552
76553                         if (selected.length > 1) {
76554                             geometry = null;
76555                             closed = null;
76556                             centroid = null;
76557                         }
76558
76559                         if (selected.length === 1 && selected[0].type === 'node') {
76560                             location = selected[0].loc;
76561                         } else {
76562                             totalNodeCount = utilGetAllNodes(selectedIDs, context.graph()).length;
76563                         }
76564
76565                         if (!location && !centroid) {
76566                             center = extent.center();
76567                         }
76568                     }
76569                 }
76570
76571                 selection.html('');
76572
76573                 if (heading) {
76574                     selection
76575                         .append('h4')
76576                         .attr('class', 'measurement-heading')
76577                         .text(heading);
76578                 }
76579
76580                 var list = selection
76581                     .append('ul');
76582                 var coordItem;
76583
76584                 if (geometry) {
76585                     list
76586                         .append('li')
76587                         .text(_t('info_panels.measurement.geometry') + ':')
76588                         .append('span')
76589                         .text(
76590                             closed ? _t('info_panels.measurement.closed_' + geometry) : _t('geometry.' + geometry)
76591                         );
76592                 }
76593
76594                 if (totalNodeCount) {
76595                     list
76596                         .append('li')
76597                         .text(_t('info_panels.measurement.node_count') + ':')
76598                         .append('span')
76599                         .text(totalNodeCount.toLocaleString(locale));
76600                 }
76601
76602                 if (area) {
76603                     list
76604                         .append('li')
76605                         .text(_t('info_panels.measurement.area') + ':')
76606                         .append('span')
76607                         .text(displayArea(area, isImperial));
76608                 }
76609
76610                 if (length) {
76611                     var lengthLabel = _t('info_panels.measurement.' + (closed ? 'perimeter' : 'length'));
76612                     list
76613                         .append('li')
76614                         .text(lengthLabel + ':')
76615                         .append('span')
76616                         .text(displayLength(length, isImperial));
76617                 }
76618
76619                 if (location) {
76620                     coordItem = list
76621                         .append('li')
76622                         .text(_t('info_panels.measurement.location') + ':');
76623                     coordItem.append('span')
76624                         .text(dmsCoordinatePair(location));
76625                     coordItem.append('span')
76626                         .text(decimalCoordinatePair(location));
76627                 }
76628
76629                 if (centroid) {
76630                     coordItem = list
76631                         .append('li')
76632                         .text(_t('info_panels.measurement.centroid') + ':');
76633                     coordItem.append('span')
76634                         .text(dmsCoordinatePair(centroid));
76635                     coordItem.append('span')
76636                         .text(decimalCoordinatePair(centroid));
76637                 }
76638
76639                 if (center) {
76640                     coordItem = list
76641                         .append('li')
76642                         .text(_t('info_panels.measurement.center') + ':');
76643                     coordItem.append('span')
76644                         .text(dmsCoordinatePair(center));
76645                     coordItem.append('span')
76646                         .text(decimalCoordinatePair(center));
76647                 }
76648
76649                 if (length || area) {
76650                     var toggle  = isImperial ? 'imperial' : 'metric';
76651                     selection
76652                         .append('a')
76653                         .text(_t('info_panels.measurement.' + toggle))
76654                         .attr('href', '#')
76655                         .attr('class', 'button button-toggle-units')
76656                         .on('click', function() {
76657                             event.preventDefault();
76658                             isImperial = !isImperial;
76659                             selection.call(redraw);
76660                         });
76661                 }
76662             }
76663
76664
76665             var panel = function(selection) {
76666                 selection.call(redraw);
76667
76668                 context.map()
76669                     .on('drawn.info-measurement', function() {
76670                         selection.call(redraw);
76671                     });
76672
76673                 context
76674                     .on('enter.info-measurement', function() {
76675                         selection.call(redraw);
76676                     });
76677             };
76678
76679             panel.off = function() {
76680                 context.map().on('drawn.info-measurement', null);
76681                 context.on('enter.info-measurement', null);
76682             };
76683
76684             panel.id = 'measurement';
76685             panel.title = _t('info_panels.measurement.title');
76686             panel.key = _t('info_panels.measurement.key');
76687
76688
76689             return panel;
76690         }
76691
76692         var uiInfoPanels = {
76693             background: uiPanelBackground,
76694             history: uiPanelHistory,
76695             location: uiPanelLocation,
76696             measurement: uiPanelMeasurement,
76697         };
76698
76699         function uiInfo(context) {
76700             var ids = Object.keys(uiInfoPanels);
76701             var wasActive = ['measurement'];
76702             var panels = {};
76703             var active = {};
76704
76705             // create panels
76706             ids.forEach(function(k) {
76707                 if (!panels[k]) {
76708                     panels[k] = uiInfoPanels[k](context);
76709                     active[k] = false;
76710                 }
76711             });
76712
76713
76714             function info(selection) {
76715
76716                 function redraw() {
76717                     var activeids = ids.filter(function(k) { return active[k]; }).sort();
76718
76719                     var containers = infoPanels.selectAll('.panel-container')
76720                         .data(activeids, function(k) { return k; });
76721
76722                     containers.exit()
76723                         .style('opacity', 1)
76724                         .transition()
76725                         .duration(200)
76726                         .style('opacity', 0)
76727                         .on('end', function(d) {
76728                             select(this)
76729                                 .call(panels[d].off)
76730                                 .remove();
76731                         });
76732
76733                     var enter = containers.enter()
76734                         .append('div')
76735                         .attr('class', function(d) { return 'fillD2 panel-container panel-container-' + d; });
76736
76737                     enter
76738                         .style('opacity', 0)
76739                         .transition()
76740                         .duration(200)
76741                         .style('opacity', 1);
76742
76743                     var title = enter
76744                         .append('div')
76745                         .attr('class', 'panel-title fillD2');
76746
76747                     title
76748                         .append('h3')
76749                         .text(function(d) { return panels[d].title; });
76750
76751                     title
76752                         .append('button')
76753                         .attr('class', 'close')
76754                         .on('click', function (d) { info.toggle(d); })
76755                         .call(svgIcon('#iD-icon-close'));
76756
76757                     enter
76758                         .append('div')
76759                         .attr('class', function(d) { return 'panel-content panel-content-' + d; });
76760
76761
76762                     // redraw the panels
76763                     infoPanels.selectAll('.panel-content')
76764                         .each(function(d) {
76765                             select(this).call(panels[d]);
76766                         });
76767                 }
76768
76769
76770                 info.toggle = function(which) {
76771                     if (event) {
76772                         event.stopImmediatePropagation();
76773                         event.preventDefault();
76774                     }
76775
76776                     var activeids = ids.filter(function(k) { return active[k]; });
76777
76778                     if (which) {  // toggle one
76779                         active[which] = !active[which];
76780                         if (activeids.length === 1 && activeids[0] === which) {  // none active anymore
76781                             wasActive = [which];
76782                         }
76783
76784                         context.container().select('.' + which + '-panel-toggle-item')
76785                             .classed('active', active[which])
76786                             .select('input')
76787                             .property('checked', active[which]);
76788
76789                     } else {      // toggle all
76790                         if (activeids.length) {
76791                             wasActive = activeids;
76792                             activeids.forEach(function(k) { active[k] = false; });
76793                         } else {
76794                             wasActive.forEach(function(k) { active[k] = true; });
76795                         }
76796                     }
76797
76798                     redraw();
76799                 };
76800
76801
76802                 var infoPanels = selection.selectAll('.info-panels')
76803                     .data([0]);
76804
76805                 infoPanels = infoPanels.enter()
76806                     .append('div')
76807                     .attr('class', 'info-panels')
76808                     .merge(infoPanels);
76809
76810                 redraw();
76811
76812                 context.keybinding()
76813                     .on(uiCmd('⌘' + _t('info_panels.key')), info.toggle);
76814
76815                 ids.forEach(function(k) {
76816                     var key = _t('info_panels.' + k + '.key', { default: null });
76817                     if (!key) { return; }
76818                     context.keybinding()
76819                         .on(uiCmd('⌘⇧' + key), function() { info.toggle(k); });
76820                 });
76821             }
76822
76823             return info;
76824         }
76825
76826         function pointBox(loc, context) {
76827             var rect = context.surfaceRect();
76828             var point = context.curtainProjection(loc);
76829             return {
76830                 left: point[0] + rect.left - 40,
76831                 top: point[1] + rect.top - 60,
76832                 width: 80,
76833                 height: 90
76834             };
76835         }
76836
76837
76838         function pad(locOrBox, padding, context) {
76839             var box;
76840             if (locOrBox instanceof Array) {
76841                 var rect = context.surfaceRect();
76842                 var point = context.curtainProjection(locOrBox);
76843                 box = {
76844                     left: point[0] + rect.left,
76845                     top: point[1] + rect.top
76846                 };
76847             } else {
76848                 box = locOrBox;
76849             }
76850
76851             return {
76852                 left: box.left - padding,
76853                 top: box.top - padding,
76854                 width: (box.width || 0) + 2 * padding,
76855                 height: (box.width || 0) + 2 * padding
76856             };
76857         }
76858
76859
76860         function icon(name, svgklass, useklass) {
76861             return '<svg class="icon ' + (svgklass || '') + '">' +
76862                  '<use xlink:href="' + name + '"' +
76863                  (useklass ? ' class="' + useklass + '"' : '') + '></use></svg>';
76864         }
76865
76866         var helpStringReplacements;
76867
76868         // Returns the localized string for `id` with a standardized set of icon, key, and
76869         // label replacements suitable for tutorials and documentation. Optionally supplemented
76870         // with custom `replacements`
76871         function helpString(id, replacements) {
76872             // only load these the first time
76873             if (!helpStringReplacements) { helpStringReplacements = {
76874                 // insert icons corresponding to various UI elements
76875                 point_icon: icon('#iD-icon-point', 'pre-text'),
76876                 line_icon: icon('#iD-icon-line', 'pre-text'),
76877                 area_icon: icon('#iD-icon-area', 'pre-text'),
76878                 note_icon: icon('#iD-icon-note', 'pre-text add-note'),
76879                 plus: icon('#iD-icon-plus', 'pre-text'),
76880                 minus: icon('#iD-icon-minus', 'pre-text'),
76881                 move_icon: icon('#iD-operation-move', 'pre-text operation'),
76882                 merge_icon: icon('#iD-operation-merge', 'pre-text operation'),
76883                 delete_icon: icon('#iD-operation-delete', 'pre-text operation'),
76884                 circularize_icon: icon('#iD-operation-circularize', 'pre-text operation'),
76885                 split_icon: icon('#iD-operation-split', 'pre-text operation'),
76886                 orthogonalize_icon: icon('#iD-operation-orthogonalize', 'pre-text operation'),
76887                 disconnect_icon: icon('#iD-operation-disconnect', 'pre-text operation'),
76888                 layers_icon: icon('#iD-icon-layers', 'pre-text'),
76889                 data_icon: icon('#iD-icon-data', 'pre-text'),
76890                 inspect: icon('#iD-icon-inspect', 'pre-text'),
76891                 help_icon: icon('#iD-icon-help', 'pre-text'),
76892                 undo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-redo' : '#iD-icon-undo', 'pre-text'),
76893                 redo_icon: icon(_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-undo' : '#iD-icon-redo', 'pre-text'),
76894                 save_icon: icon('#iD-icon-save', 'pre-text'),
76895                 leftclick: icon('#iD-walkthrough-mouse-left', 'pre-text operation'),
76896                 rightclick: icon('#iD-walkthrough-mouse-right', 'pre-text operation'),
76897                 mousewheel_icon: icon('#iD-walkthrough-mousewheel', 'pre-text operation'),
76898                 tap_icon: icon('#iD-walkthrough-tap', 'pre-text operation'),
76899                 doubletap_icon: icon('#iD-walkthrough-doubletap', 'pre-text operation'),
76900                 longpress_icon: icon('#iD-walkthrough-longpress', 'pre-text operation'),
76901                 touchdrag_icon: icon('#iD-walkthrough-touchdrag', 'pre-text operation'),
76902                 pinch_icon: icon('#iD-walkthrough-pinch-apart', 'pre-text operation'),
76903
76904                 // insert keys; may be localized and platform-dependent
76905                 shift: uiCmd.display('⇧'),
76906                 alt: uiCmd.display('⌥'),
76907                 return: uiCmd.display('↵'),
76908                 esc: _t('shortcuts.key.esc'),
76909                 space: _t('shortcuts.key.space'),
76910                 add_note_key: _t('modes.add_note.key'),
76911                 help_key: _t('help.key'),
76912                 shortcuts_key: _t('shortcuts.toggle.key'),
76913
76914                 // reference localized UI labels directly so that they'll always match
76915                 save: _t('save.title'),
76916                 undo: _t('undo.title'),
76917                 redo: _t('redo.title'),
76918                 upload: _t('commit.save'),
76919                 point: _t('modes.add_point.title'),
76920                 line: _t('modes.add_line.title'),
76921                 area: _t('modes.add_area.title'),
76922                 note: _t('modes.add_note.title'),
76923                 delete: _t('operations.delete.title'),
76924                 move: _t('operations.move.title'),
76925                 orthogonalize: _t('operations.orthogonalize.title'),
76926                 circularize: _t('operations.circularize.title'),
76927                 merge: _t('operations.merge.title'),
76928                 disconnect: _t('operations.disconnect.title'),
76929                 split: _t('operations.split.title'),
76930                 map_data: _t('map_data.title'),
76931                 osm_notes: _t('map_data.layers.notes.title'),
76932                 fields: _t('inspector.fields'),
76933                 tags: _t('inspector.tags'),
76934                 relations: _t('inspector.relations'),
76935                 new_relation: _t('inspector.new_relation'),
76936                 turn_restrictions: _t('presets.fields.restrictions.label'),
76937                 background_settings: _t('background.description'),
76938                 imagery_offset: _t('background.fix_misalignment'),
76939                 start_the_walkthrough: _t('splash.walkthrough'),
76940                 help: _t('help.title'),
76941                 ok: _t('intro.ok')
76942             }; }
76943
76944             var reps;
76945             if (replacements) {
76946                 reps = Object.assign(replacements, helpStringReplacements);
76947             } else {
76948                 reps = helpStringReplacements;
76949             }
76950
76951             return _t(id, reps)
76952                  // use keyboard key styling for shortcuts
76953                 .replace(/\`(.*?)\`/g, '<kbd>$1</kbd>');
76954         }
76955
76956
76957         function slugify(text) {
76958             return text.toString().toLowerCase()
76959                 .replace(/\s+/g, '-')           // Replace spaces with -
76960                 .replace(/[^\w\-]+/g, '')       // Remove all non-word chars
76961                 .replace(/\-\-+/g, '-')         // Replace multiple - with single -
76962                 .replace(/^-+/, '')             // Trim - from start of text
76963                 .replace(/-+$/, '');            // Trim - from end of text
76964         }
76965
76966
76967         // console warning for missing walkthrough names
76968         var missingStrings = {};
76969         function checkKey(key, text) {
76970             if (_t(key, { default: undefined}) === undefined) {
76971                 if (missingStrings.hasOwnProperty(key)) { return; }  // warn once
76972                 missingStrings[key] = text;
76973                 var missing = key + ': ' + text;
76974                 if (typeof console !== 'undefined') { console.log(missing); } // eslint-disable-line
76975             }
76976         }
76977
76978
76979         function localize(obj) {
76980             var key;
76981
76982             // Assign name if entity has one..
76983             var name = obj.tags && obj.tags.name;
76984             if (name) {
76985                 key = 'intro.graph.name.' + slugify(name);
76986                 obj.tags.name = _t(key, { default: name });
76987                 checkKey(key, name);
76988             }
76989
76990             // Assign street name if entity has one..
76991             var street = obj.tags && obj.tags['addr:street'];
76992             if (street) {
76993                 key = 'intro.graph.name.' + slugify(street);
76994                 obj.tags['addr:street'] = _t(key, { default: street });
76995                 checkKey(key, street);
76996
76997                 // Add address details common across walkthrough..
76998                 var addrTags = [
76999                     'block_number', 'city', 'county', 'district', 'hamlet', 'neighbourhood',
77000                     'postcode', 'province', 'quarter', 'state', 'subdistrict', 'suburb'
77001                 ];
77002                 addrTags.forEach(function(k) {
77003                     var key = 'intro.graph.' + k;
77004                     var tag = 'addr:' + k;
77005                     var val = obj.tags && obj.tags[tag];
77006                     var str = _t(key, { default: val });
77007
77008                     if (str) {
77009                         if (str.match(/^<.*>$/) !== null) {
77010                             delete obj.tags[tag];
77011                         } else {
77012                             obj.tags[tag] = str;
77013                         }
77014                     }
77015                 });
77016             }
77017
77018             return obj;
77019         }
77020
77021
77022         // Used to detect squareness.. some duplicataion of code from actionOrthogonalize.
77023         function isMostlySquare(points) {
77024             // note: uses 15 here instead of the 12 from actionOrthogonalize because
77025             // actionOrthogonalize can actually straighten some larger angles as it iterates
77026             var threshold = 15; // degrees within right or straight
77027             var lowerBound = Math.cos((90 - threshold) * Math.PI / 180);  // near right
77028             var upperBound = Math.cos(threshold * Math.PI / 180);         // near straight
77029
77030             for (var i = 0; i < points.length; i++) {
77031                 var a = points[(i - 1 + points.length) % points.length];
77032                 var origin = points[i];
77033                 var b = points[(i + 1) % points.length];
77034
77035                 var dotp = geoVecNormalizedDot(a, b, origin);
77036                 var mag = Math.abs(dotp);
77037                 if (mag > lowerBound && mag < upperBound) {
77038                     return false;
77039                 }
77040             }
77041
77042             return true;
77043         }
77044
77045
77046         function selectMenuItem(context, operation) {
77047             return context.container().select('.edit-menu .edit-menu-item-' + operation);
77048         }
77049
77050
77051         function transitionTime(point1, point2) {
77052             var distance = geoSphericalDistance(point1, point2);
77053             if (distance === 0)
77054                 { return 0; }
77055             else if (distance < 80)
77056                 { return 500; }
77057             else
77058                 { return 1000; }
77059         }
77060
77061         // Tooltips and svg mask used to highlight certain features
77062         function uiCurtain(containerNode) {
77063
77064             var surface = select(null),
77065                 tooltip = select(null),
77066                 darkness = select(null);
77067
77068             function curtain(selection) {
77069                 surface = selection
77070                     .append('svg')
77071                     .attr('class', 'curtain')
77072                     .style('top', 0)
77073                     .style('left', 0);
77074
77075                 darkness = surface.append('path')
77076                     .attr('x', 0)
77077                     .attr('y', 0)
77078                     .attr('class', 'curtain-darkness');
77079
77080                 select(window).on('resize.curtain', resize);
77081
77082                 tooltip = selection.append('div')
77083                     .attr('class', 'tooltip');
77084
77085                 tooltip
77086                     .append('div')
77087                     .attr('class', 'popover-arrow');
77088
77089                 tooltip
77090                     .append('div')
77091                     .attr('class', 'popover-inner');
77092
77093                 resize();
77094
77095
77096                 function resize() {
77097                     surface
77098                         .attr('width', containerNode.clientWidth)
77099                         .attr('height', containerNode.clientHeight);
77100                     curtain.cut(darkness.datum());
77101                 }
77102             }
77103
77104
77105             /**
77106              * Reveal cuts the curtain to highlight the given box,
77107              * and shows a tooltip with instructions next to the box.
77108              *
77109              * @param  {String|ClientRect} [box]   box used to cut the curtain
77110              * @param  {String}    [text]          text for a tooltip
77111              * @param  {Object}    [options]
77112              * @param  {string}    [options.tooltipClass]    optional class to add to the tooltip
77113              * @param  {integer}   [options.duration]        transition time in milliseconds
77114              * @param  {string}    [options.buttonText]      if set, create a button with this text label
77115              * @param  {function}  [options.buttonCallback]  if set, the callback for the button
77116              * @param  {function}  [options.padding]         extra margin in px to put around bbox
77117              * @param  {String|ClientRect} [options.tooltipBox]  box for tooltip position, if different from box for the curtain
77118              */
77119             curtain.reveal = function(box, text, options) {
77120                 options = options || {};
77121
77122                 if (typeof box === 'string') {
77123                     box = select(box).node();
77124                 }
77125                 if (box && box.getBoundingClientRect) {
77126                     box = copyBox(box.getBoundingClientRect());
77127                     var containerRect = containerNode.getBoundingClientRect();
77128                     box.top -= containerRect.top;
77129                     box.left -= containerRect.left;
77130                 }
77131                 if (box && options.padding) {
77132                     box.top -= options.padding;
77133                     box.left -= options.padding;
77134                     box.bottom += options.padding;
77135                     box.right += options.padding;
77136                     box.height += options.padding * 2;
77137                     box.width += options.padding * 2;
77138                 }
77139
77140                 var tooltipBox;
77141                 if (options.tooltipBox) {
77142                     tooltipBox = options.tooltipBox;
77143                     if (typeof tooltipBox === 'string') {
77144                         tooltipBox = select(tooltipBox).node();
77145                     }
77146                     if (tooltipBox && tooltipBox.getBoundingClientRect) {
77147                         tooltipBox = copyBox(tooltipBox.getBoundingClientRect());
77148                     }
77149                 } else {
77150                     tooltipBox = box;
77151                 }
77152
77153                 if (tooltipBox && text) {
77154                     // pseudo markdown bold text for the instruction section..
77155                     var parts = text.split('**');
77156                     var html = parts[0] ? '<span>' + parts[0] + '</span>' : '';
77157                     if (parts[1]) {
77158                         html += '<span class="instruction">' + parts[1] + '</span>';
77159                     }
77160
77161                     html = html.replace(/\*(.*?)\*/g, '<em>$1</em>');   // emphasis
77162                     html = html.replace(/\{br\}/g, '<br/><br/>');       // linebreak
77163
77164                     if (options.buttonText && options.buttonCallback) {
77165                         html += '<div class="button-section">' +
77166                             '<button href="#" class="button action">' + options.buttonText + '</button></div>';
77167                     }
77168
77169                     var classes = 'curtain-tooltip popover tooltip arrowed in ' + (options.tooltipClass || '');
77170                     tooltip
77171                         .classed(classes, true)
77172                         .selectAll('.popover-inner')
77173                         .html(html);
77174
77175                     if (options.buttonText && options.buttonCallback) {
77176                         var button = tooltip.selectAll('.button-section .button.action');
77177                         button
77178                             .on('click', function() {
77179                                 event.preventDefault();
77180                                 options.buttonCallback();
77181                             });
77182                     }
77183
77184                     var tip = copyBox(tooltip.node().getBoundingClientRect()),
77185                         w = containerNode.clientWidth,
77186                         h = containerNode.clientHeight,
77187                         tooltipWidth = 200,
77188                         tooltipArrow = 5,
77189                         side, pos;
77190
77191
77192                     // hack: this will have bottom placement,
77193                     // so need to reserve extra space for the tooltip illustration.
77194                     if (options.tooltipClass === 'intro-mouse') {
77195                         tip.height += 80;
77196                     }
77197
77198                     // trim box dimensions to just the portion that fits in the container..
77199                     if (tooltipBox.top + tooltipBox.height > h) {
77200                         tooltipBox.height -= (tooltipBox.top + tooltipBox.height - h);
77201                     }
77202                     if (tooltipBox.left + tooltipBox.width > w) {
77203                         tooltipBox.width -= (tooltipBox.left + tooltipBox.width - w);
77204                     }
77205
77206                     // determine tooltip placement..
77207
77208                     if (tooltipBox.top + tooltipBox.height < 100) {
77209                         // tooltip below box..
77210                         side = 'bottom';
77211                         pos = [
77212                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77213                             tooltipBox.top + tooltipBox.height
77214                         ];
77215
77216                     } else if (tooltipBox.top > h - 140) {
77217                         // tooltip above box..
77218                         side = 'top';
77219                         pos = [
77220                             tooltipBox.left + tooltipBox.width / 2 - tip.width / 2,
77221                             tooltipBox.top - tip.height
77222                         ];
77223
77224                     } else {
77225                         // tooltip to the side of the tooltipBox..
77226                         var tipY = tooltipBox.top + tooltipBox.height / 2 - tip.height / 2;
77227
77228                         if (_mainLocalizer.textDirection() === 'rtl') {
77229                             if (tooltipBox.left - tooltipWidth - tooltipArrow < 70) {
77230                                 side = 'right';
77231                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77232
77233                             } else {
77234                                 side = 'left';
77235                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77236                             }
77237
77238                         } else {
77239                             if (tooltipBox.left + tooltipBox.width + tooltipArrow + tooltipWidth > w - 70) {
77240                                 side = 'left';
77241                                 pos = [tooltipBox.left - tooltipWidth - tooltipArrow, tipY];
77242                             }
77243                             else {
77244                                 side = 'right';
77245                                 pos = [tooltipBox.left + tooltipBox.width + tooltipArrow, tipY];
77246                             }
77247                         }
77248                     }
77249
77250                     if (options.duration !== 0 || !tooltip.classed(side)) {
77251                         tooltip.call(uiToggle(true));
77252                     }
77253
77254                     tooltip
77255                         .style('top', pos[1] + 'px')
77256                         .style('left', pos[0] + 'px')
77257                         .attr('class', classes + ' ' + side);
77258
77259
77260                     // shift popover-inner if it is very close to the top or bottom edge
77261                     // (doesn't affect the placement of the popover-arrow)
77262                     var shiftY = 0;
77263                     if (side === 'left' || side === 'right') {
77264                         if (pos[1] < 60) {
77265                             shiftY = 60 - pos[1];
77266                         }
77267                         else if (pos[1] + tip.height > h - 100) {
77268                             shiftY = h - pos[1] - tip.height - 100;
77269                         }
77270                     }
77271                     tooltip.selectAll('.popover-inner')
77272                         .style('top', shiftY + 'px');
77273
77274                 } else {
77275                     tooltip
77276                         .classed('in', false)
77277                         .call(uiToggle(false));
77278                 }
77279
77280                 curtain.cut(box, options.duration);
77281
77282                 return tooltip;
77283             };
77284
77285
77286             curtain.cut = function(datum, duration) {
77287                 darkness.datum(datum)
77288                     .interrupt();
77289
77290                 var selection;
77291                 if (duration === 0) {
77292                     selection = darkness;
77293                 } else {
77294                     selection = darkness
77295                         .transition()
77296                         .duration(duration || 600)
77297                         .ease(linear$1);
77298                 }
77299
77300                 selection
77301                     .attr('d', function(d) {
77302                         var containerWidth = containerNode.clientWidth;
77303                         var containerHeight = containerNode.clientHeight;
77304                         var string = 'M 0,0 L 0,' + containerHeight + ' L ' +
77305                             containerWidth + ',' + containerHeight + 'L' +
77306                             containerWidth + ',0 Z';
77307
77308                         if (!d) { return string; }
77309                         return string + 'M' +
77310                             d.left + ',' + d.top + 'L' +
77311                             d.left + ',' + (d.top + d.height) + 'L' +
77312                             (d.left + d.width) + ',' + (d.top + d.height) + 'L' +
77313                             (d.left + d.width) + ',' + (d.top) + 'Z';
77314
77315                     });
77316             };
77317
77318
77319             curtain.remove = function() {
77320                 surface.remove();
77321                 tooltip.remove();
77322                 select(window).on('resize.curtain', null);
77323             };
77324
77325
77326             // ClientRects are immutable, so copy them to an object,
77327             // in case we need to trim the height/width.
77328             function copyBox(src) {
77329                 return {
77330                     top: src.top,
77331                     right: src.right,
77332                     bottom: src.bottom,
77333                     left: src.left,
77334                     width: src.width,
77335                     height: src.height
77336                 };
77337             }
77338
77339
77340             return curtain;
77341         }
77342
77343         function uiIntroWelcome(context, reveal) {
77344             var dispatch$1 = dispatch('done');
77345
77346             var chapter = {
77347                 title: 'intro.welcome.title'
77348             };
77349
77350
77351             function welcome() {
77352                 context.map().centerZoom([-85.63591, 41.94285], 19);
77353                 reveal('.intro-nav-wrap .chapter-welcome',
77354                     helpString('intro.welcome.welcome'),
77355                     { buttonText: _t('intro.ok'), buttonCallback: practice }
77356                 );
77357             }
77358
77359             function practice() {
77360                 reveal('.intro-nav-wrap .chapter-welcome',
77361                     helpString('intro.welcome.practice'),
77362                     { buttonText: _t('intro.ok'), buttonCallback: words }
77363                 );
77364             }
77365
77366             function words() {
77367                 reveal('.intro-nav-wrap .chapter-welcome',
77368                     helpString('intro.welcome.words'),
77369                     { buttonText: _t('intro.ok'), buttonCallback: chapters }
77370                 );
77371             }
77372
77373
77374             function chapters() {
77375                 dispatch$1.call('done');
77376                 reveal('.intro-nav-wrap .chapter-navigation',
77377                     helpString('intro.welcome.chapters', { next: _t('intro.navigation.title') })
77378                 );
77379             }
77380
77381
77382             chapter.enter = function() {
77383                 welcome();
77384             };
77385
77386
77387             chapter.exit = function() {
77388                 context.container().select('.curtain-tooltip.intro-mouse')
77389                     .selectAll('.counter')
77390                     .remove();
77391             };
77392
77393
77394             chapter.restart = function() {
77395                 chapter.exit();
77396                 chapter.enter();
77397             };
77398
77399
77400             return utilRebind(chapter, dispatch$1, 'on');
77401         }
77402
77403         function uiIntroNavigation(context, reveal) {
77404             var dispatch$1 = dispatch('done');
77405             var timeouts = [];
77406             var hallId = 'n2061';
77407             var townHall = [-85.63591, 41.94285];
77408             var springStreetId = 'w397';
77409             var springStreetEndId = 'n1834';
77410             var springStreet = [-85.63582, 41.94255];
77411             var onewayField = _mainPresetIndex.field('oneway');
77412             var maxspeedField = _mainPresetIndex.field('maxspeed');
77413
77414
77415             var chapter = {
77416                 title: 'intro.navigation.title'
77417             };
77418
77419
77420             function timeout(f, t) {
77421                 timeouts.push(window.setTimeout(f, t));
77422             }
77423
77424
77425             function eventCancel() {
77426                 event.stopPropagation();
77427                 event.preventDefault();
77428             }
77429
77430
77431             function isTownHallSelected() {
77432                 var ids = context.selectedIDs();
77433                 return ids.length === 1 && ids[0] === hallId;
77434             }
77435
77436
77437             function dragMap() {
77438                 context.enter(modeBrowse(context));
77439                 context.history().reset('initial');
77440
77441                 var msec = transitionTime(townHall, context.map().center());
77442                 if (msec) { reveal(null, null, { duration: 0 }); }
77443                 context.map().centerZoomEase(townHall, 19, msec);
77444
77445                 timeout(function() {
77446                     var centerStart = context.map().center();
77447
77448                     var textId = context.lastPointerType() === 'mouse' ? 'drag' : 'drag_touch';
77449                     var dragString = helpString('intro.navigation.map_info') + '{br}' + helpString('intro.navigation.' + textId);
77450                     reveal('.surface', dragString);
77451                     context.map().on('drawn.intro', function() {
77452                         reveal('.surface', dragString, { duration: 0 });
77453                     });
77454
77455                     context.map().on('move.intro', function() {
77456                         var centerNow = context.map().center();
77457                         if (centerStart[0] !== centerNow[0] || centerStart[1] !== centerNow[1]) {
77458                             context.map().on('move.intro', null);
77459                             timeout(function() { continueTo(zoomMap); }, 3000);
77460                         }
77461                     });
77462
77463                 }, msec + 100);
77464
77465                 function continueTo(nextStep) {
77466                     context.map().on('move.intro drawn.intro', null);
77467                     nextStep();
77468                 }
77469             }
77470
77471
77472             function zoomMap() {
77473                 var zoomStart = context.map().zoom();
77474
77475                 var textId = context.lastPointerType() === 'mouse' ? 'zoom' : 'zoom_touch';
77476                 var zoomString = helpString('intro.navigation.' + textId);
77477
77478                 reveal('.surface', zoomString);
77479
77480                 context.map().on('drawn.intro', function() {
77481                     reveal('.surface', zoomString, { duration: 0 });
77482                 });
77483
77484                 context.map().on('move.intro', function() {
77485                     if (context.map().zoom() !== zoomStart) {
77486                         context.map().on('move.intro', null);
77487                         timeout(function() { continueTo(features); }, 3000);
77488                     }
77489                 });
77490
77491                 function continueTo(nextStep) {
77492                     context.map().on('move.intro drawn.intro', null);
77493                     nextStep();
77494                 }
77495             }
77496
77497
77498             function features() {
77499                 var onClick = function() { continueTo(pointsLinesAreas); };
77500
77501                 reveal('.surface', helpString('intro.navigation.features'),
77502                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77503                 );
77504
77505                 context.map().on('drawn.intro', function() {
77506                     reveal('.surface', helpString('intro.navigation.features'),
77507                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77508                     );
77509                 });
77510
77511                 function continueTo(nextStep) {
77512                     context.map().on('drawn.intro', null);
77513                     nextStep();
77514                 }
77515             }
77516
77517             function pointsLinesAreas() {
77518                 var onClick = function() { continueTo(nodesWays); };
77519
77520                 reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77521                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77522                 );
77523
77524                 context.map().on('drawn.intro', function() {
77525                     reveal('.surface', helpString('intro.navigation.points_lines_areas'),
77526                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77527                     );
77528                 });
77529
77530                 function continueTo(nextStep) {
77531                     context.map().on('drawn.intro', null);
77532                     nextStep();
77533                 }
77534             }
77535
77536             function nodesWays() {
77537                 var onClick = function() { continueTo(clickTownHall); };
77538
77539                 reveal('.surface', helpString('intro.navigation.nodes_ways'),
77540                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77541                 );
77542
77543                 context.map().on('drawn.intro', function() {
77544                     reveal('.surface', helpString('intro.navigation.nodes_ways'),
77545                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77546                     );
77547                 });
77548
77549                 function continueTo(nextStep) {
77550                     context.map().on('drawn.intro', null);
77551                     nextStep();
77552                 }
77553             }
77554
77555             function clickTownHall() {
77556                 context.enter(modeBrowse(context));
77557                 context.history().reset('initial');
77558
77559                 var entity = context.hasEntity(hallId);
77560                 if (!entity) { return; }
77561                 reveal(null, null, { duration: 0 });
77562                 context.map().centerZoomEase(entity.loc, 19, 500);
77563
77564                 timeout(function() {
77565                     var entity = context.hasEntity(hallId);
77566                     if (!entity) { return; }
77567                     var box = pointBox(entity.loc, context);
77568                     var textId = context.lastPointerType() === 'mouse' ? 'click_townhall' : 'tap_townhall';
77569                     reveal(box, helpString('intro.navigation.' + textId));
77570
77571                     context.map().on('move.intro drawn.intro', function() {
77572                         var entity = context.hasEntity(hallId);
77573                         if (!entity) { return; }
77574                         var box = pointBox(entity.loc, context);
77575                         reveal(box, helpString('intro.navigation.' + textId), { duration: 0 });
77576                     });
77577
77578                     context.on('enter.intro', function() {
77579                         if (isTownHallSelected()) { continueTo(selectedTownHall); }
77580                     });
77581
77582                 }, 550);  // after centerZoomEase
77583
77584                 context.history().on('change.intro', function() {
77585                     if (!context.hasEntity(hallId)) {
77586                         continueTo(clickTownHall);
77587                     }
77588                 });
77589
77590                 function continueTo(nextStep) {
77591                     context.on('enter.intro', null);
77592                     context.map().on('move.intro drawn.intro', null);
77593                     context.history().on('change.intro', null);
77594                     nextStep();
77595                 }
77596             }
77597
77598
77599             function selectedTownHall() {
77600                 if (!isTownHallSelected()) { return clickTownHall(); }
77601
77602                 var entity = context.hasEntity(hallId);
77603                 if (!entity) { return clickTownHall(); }
77604
77605                 var box = pointBox(entity.loc, context);
77606                 var onClick = function() { continueTo(editorTownHall); };
77607
77608                 reveal(box, helpString('intro.navigation.selected_townhall'),
77609                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77610                 );
77611
77612                 context.map().on('move.intro drawn.intro', function() {
77613                     var entity = context.hasEntity(hallId);
77614                     if (!entity) { return; }
77615                     var box = pointBox(entity.loc, context);
77616                     reveal(box, helpString('intro.navigation.selected_townhall'),
77617                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77618                     );
77619                 });
77620
77621                 context.history().on('change.intro', function() {
77622                     if (!context.hasEntity(hallId)) {
77623                         continueTo(clickTownHall);
77624                     }
77625                 });
77626
77627                 function continueTo(nextStep) {
77628                     context.map().on('move.intro drawn.intro', null);
77629                     context.history().on('change.intro', null);
77630                     nextStep();
77631                 }
77632             }
77633
77634
77635             function editorTownHall() {
77636                 if (!isTownHallSelected()) { return clickTownHall(); }
77637
77638                 // disallow scrolling
77639                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77640
77641                 var onClick = function() { continueTo(presetTownHall); };
77642
77643                 reveal('.entity-editor-pane',
77644                     helpString('intro.navigation.editor_townhall'),
77645                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77646                 );
77647
77648                 context.on('exit.intro', function() {
77649                     continueTo(clickTownHall);
77650                 });
77651
77652                 context.history().on('change.intro', function() {
77653                     if (!context.hasEntity(hallId)) {
77654                         continueTo(clickTownHall);
77655                     }
77656                 });
77657
77658                 function continueTo(nextStep) {
77659                     context.on('exit.intro', null);
77660                     context.history().on('change.intro', null);
77661                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77662                     nextStep();
77663                 }
77664             }
77665
77666
77667             function presetTownHall() {
77668                 if (!isTownHallSelected()) { return clickTownHall(); }
77669
77670                 // reset pane, in case user happened to change it..
77671                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77672                 // disallow scrolling
77673                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77674
77675                 // preset match, in case the user happened to change it.
77676                 var entity = context.entity(context.selectedIDs()[0]);
77677                 var preset = _mainPresetIndex.match(entity, context.graph());
77678
77679                 var onClick = function() { continueTo(fieldsTownHall); };
77680
77681                 reveal('.entity-editor-pane .section-feature-type',
77682                     helpString('intro.navigation.preset_townhall', { preset: preset.name() }),
77683                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77684                 );
77685
77686                 context.on('exit.intro', function() {
77687                     continueTo(clickTownHall);
77688                 });
77689
77690                 context.history().on('change.intro', function() {
77691                     if (!context.hasEntity(hallId)) {
77692                         continueTo(clickTownHall);
77693                     }
77694                 });
77695
77696                 function continueTo(nextStep) {
77697                     context.on('exit.intro', null);
77698                     context.history().on('change.intro', null);
77699                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77700                     nextStep();
77701                 }
77702             }
77703
77704
77705             function fieldsTownHall() {
77706                 if (!isTownHallSelected()) { return clickTownHall(); }
77707
77708                 // reset pane, in case user happened to change it..
77709                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
77710                 // disallow scrolling
77711                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
77712
77713                 var onClick = function() { continueTo(closeTownHall); };
77714
77715                 reveal('.entity-editor-pane .section-preset-fields',
77716                     helpString('intro.navigation.fields_townhall'),
77717                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
77718                 );
77719
77720                 context.on('exit.intro', function() {
77721                     continueTo(clickTownHall);
77722                 });
77723
77724                 context.history().on('change.intro', function() {
77725                     if (!context.hasEntity(hallId)) {
77726                         continueTo(clickTownHall);
77727                     }
77728                 });
77729
77730                 function continueTo(nextStep) {
77731                     context.on('exit.intro', null);
77732                     context.history().on('change.intro', null);
77733                     context.container().select('.inspector-wrap').on('wheel.intro', null);
77734                     nextStep();
77735                 }
77736             }
77737
77738
77739             function closeTownHall() {
77740                 if (!isTownHallSelected()) { return clickTownHall(); }
77741
77742                 var selector = '.entity-editor-pane button.close svg use';
77743                 var href = select(selector).attr('href') || '#iD-icon-close';
77744
77745                 reveal('.entity-editor-pane',
77746                     helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') })
77747                 );
77748
77749                 context.on('exit.intro', function() {
77750                     continueTo(searchStreet);
77751                 });
77752
77753                 context.history().on('change.intro', function() {
77754                     // update the close icon in the tooltip if the user edits something.
77755                     var selector = '.entity-editor-pane button.close svg use';
77756                     var href = select(selector).attr('href') || '#iD-icon-close';
77757
77758                     reveal('.entity-editor-pane',
77759                         helpString('intro.navigation.close_townhall', { button: icon(href, 'pre-text') }),
77760                         { duration: 0 }
77761                     );
77762                 });
77763
77764                 function continueTo(nextStep) {
77765                     context.on('exit.intro', null);
77766                     context.history().on('change.intro', null);
77767                     nextStep();
77768                 }
77769             }
77770
77771
77772             function searchStreet() {
77773                 context.enter(modeBrowse(context));
77774                 context.history().reset('initial');  // ensure spring street exists
77775
77776                 var msec = transitionTime(springStreet, context.map().center());
77777                 if (msec) { reveal(null, null, { duration: 0 }); }
77778                 context.map().centerZoomEase(springStreet, 19, msec);  // ..and user can see it
77779
77780                 timeout(function() {
77781                     reveal('.search-header input',
77782                         helpString('intro.navigation.search_street', { name: _t('intro.graph.name.spring-street') })
77783                     );
77784
77785                     context.container().select('.search-header input')
77786                         .on('keyup.intro', checkSearchResult);
77787                 }, msec + 100);
77788             }
77789
77790
77791             function checkSearchResult() {
77792                 var first = context.container().select('.feature-list-item:nth-child(0n+2)');  // skip "No Results" item
77793                 var firstName = first.select('.entity-name');
77794                 var name = _t('intro.graph.name.spring-street');
77795
77796                 if (!firstName.empty() && firstName.text() === name) {
77797                     reveal(first.node(),
77798                         helpString('intro.navigation.choose_street', { name: name }),
77799                         { duration: 300 }
77800                     );
77801
77802                     context.on('exit.intro', function() {
77803                         continueTo(selectedStreet);
77804                     });
77805
77806                     context.container().select('.search-header input')
77807                         .on('keydown.intro', eventCancel, true)
77808                         .on('keyup.intro', null);
77809                 }
77810
77811                 function continueTo(nextStep) {
77812                     context.on('exit.intro', null);
77813                     context.container().select('.search-header input')
77814                         .on('keydown.intro', null)
77815                         .on('keyup.intro', null);
77816                     nextStep();
77817                 }
77818             }
77819
77820
77821             function selectedStreet() {
77822                 if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77823                     return searchStreet();
77824                 }
77825
77826                 var onClick = function() { continueTo(editorStreet); };
77827                 var entity = context.entity(springStreetEndId);
77828                 var box = pointBox(entity.loc, context);
77829                 box.height = 500;
77830
77831                 reveal(box,
77832                     helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77833                     { duration: 600, buttonText: _t('intro.ok'), buttonCallback: onClick }
77834                 );
77835
77836                 timeout(function() {
77837                     context.map().on('move.intro drawn.intro', function() {
77838                         var entity = context.hasEntity(springStreetEndId);
77839                         if (!entity) { return; }
77840                         var box = pointBox(entity.loc, context);
77841                         box.height = 500;
77842                         reveal(box,
77843                             helpString('intro.navigation.selected_street', { name: _t('intro.graph.name.spring-street') }),
77844                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
77845                         );
77846                     });
77847                 }, 600);  // after reveal.
77848
77849                 context.on('enter.intro', function(mode) {
77850                     if (!context.hasEntity(springStreetId)) {
77851                         return continueTo(searchStreet);
77852                     }
77853                     var ids = context.selectedIDs();
77854                     if (mode.id !== 'select' || !ids.length || ids[0] !== springStreetId) {
77855                         // keep Spring Street selected..
77856                         context.enter(modeSelect(context, [springStreetId]));
77857                     }
77858                 });
77859
77860                 context.history().on('change.intro', function() {
77861                     if (!context.hasEntity(springStreetEndId) || !context.hasEntity(springStreetId)) {
77862                         timeout(function() {
77863                             continueTo(searchStreet);
77864                         }, 300);  // after any transition (e.g. if user deleted intersection)
77865                     }
77866                 });
77867
77868                 function continueTo(nextStep) {
77869                     context.map().on('move.intro drawn.intro', null);
77870                     context.on('enter.intro', null);
77871                     context.history().on('change.intro', null);
77872                     nextStep();
77873                 }
77874             }
77875
77876
77877             function editorStreet() {
77878                 var selector = '.entity-editor-pane button.close svg use';
77879                 var href = select(selector).attr('href') || '#iD-icon-close';
77880
77881                 reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77882                     helpString('intro.navigation.editor_street', {
77883                         button: icon(href, 'pre-text'),
77884                         field1: onewayField.label(),
77885                         field2: maxspeedField.label()
77886                     }));
77887
77888                 context.on('exit.intro', function() {
77889                     continueTo(play);
77890                 });
77891
77892                 context.history().on('change.intro', function() {
77893                     // update the close icon in the tooltip if the user edits something.
77894                     var selector = '.entity-editor-pane button.close svg use';
77895                     var href = select(selector).attr('href') || '#iD-icon-close';
77896
77897                     reveal('.entity-editor-pane', helpString('intro.navigation.street_different_fields') + '{br}' +
77898                         helpString('intro.navigation.editor_street', {
77899                             button: icon(href, 'pre-text'),
77900                             field1: onewayField.label(),
77901                             field2: maxspeedField.label()
77902                         }), { duration: 0 }
77903                     );
77904                 });
77905
77906                 function continueTo(nextStep) {
77907                     context.on('exit.intro', null);
77908                     context.history().on('change.intro', null);
77909                     nextStep();
77910                 }
77911             }
77912
77913
77914             function play() {
77915                 dispatch$1.call('done');
77916                 reveal('.ideditor',
77917                     helpString('intro.navigation.play', { next: _t('intro.points.title') }), {
77918                         tooltipBox: '.intro-nav-wrap .chapter-point',
77919                         buttonText: _t('intro.ok'),
77920                         buttonCallback: function() { reveal('.ideditor'); }
77921                     }
77922                 );
77923             }
77924
77925
77926             chapter.enter = function() {
77927                 dragMap();
77928             };
77929
77930
77931             chapter.exit = function() {
77932                 timeouts.forEach(window.clearTimeout);
77933                 context.on('enter.intro exit.intro', null);
77934                 context.map().on('move.intro drawn.intro', null);
77935                 context.history().on('change.intro', null);
77936                 context.container().select('.inspector-wrap').on('wheel.intro', null);
77937                 context.container().select('.search-header input').on('keydown.intro keyup.intro', null);
77938             };
77939
77940
77941             chapter.restart = function() {
77942                 chapter.exit();
77943                 chapter.enter();
77944             };
77945
77946
77947             return utilRebind(chapter, dispatch$1, 'on');
77948         }
77949
77950         function uiIntroPoint(context, reveal) {
77951             var dispatch$1 = dispatch('done');
77952             var timeouts = [];
77953             var intersection = [-85.63279, 41.94394];
77954             var building = [-85.632422, 41.944045];
77955             var cafePreset = _mainPresetIndex.item('amenity/cafe');
77956             var _pointID = null;
77957
77958
77959             var chapter = {
77960                 title: 'intro.points.title'
77961             };
77962
77963
77964             function timeout(f, t) {
77965                 timeouts.push(window.setTimeout(f, t));
77966             }
77967
77968
77969             function eventCancel() {
77970                 event.stopPropagation();
77971                 event.preventDefault();
77972             }
77973
77974
77975             function addPoint() {
77976                 context.enter(modeBrowse(context));
77977                 context.history().reset('initial');
77978
77979                 var msec = transitionTime(intersection, context.map().center());
77980                 if (msec) { reveal(null, null, { duration: 0 }); }
77981                 context.map().centerZoomEase(intersection, 19, msec);
77982
77983                 timeout(function() {
77984                     var tooltip = reveal('button.add-point',
77985                         helpString('intro.points.points_info') + '{br}' + helpString('intro.points.add_point'));
77986
77987                     _pointID = null;
77988
77989                     tooltip.selectAll('.popover-inner')
77990                         .insert('svg', 'span')
77991                         .attr('class', 'tooltip-illustration')
77992                         .append('use')
77993                         .attr('xlink:href', '#iD-graphic-points');
77994
77995                     context.on('enter.intro', function(mode) {
77996                         if (mode.id !== 'add-point') { return; }
77997                         continueTo(placePoint);
77998                     });
77999                 }, msec + 100);
78000
78001                 function continueTo(nextStep) {
78002                     context.on('enter.intro', null);
78003                     nextStep();
78004                 }
78005             }
78006
78007
78008             function placePoint() {
78009                 if (context.mode().id !== 'add-point') {
78010                     return chapter.restart();
78011                 }
78012
78013                 var pointBox = pad(building, 150, context);
78014                 var textId = context.lastPointerType() === 'mouse' ? 'place_point' : 'place_point_touch';
78015                 reveal(pointBox, helpString('intro.points.' + textId));
78016
78017                 context.map().on('move.intro drawn.intro', function() {
78018                     pointBox = pad(building, 150, context);
78019                     reveal(pointBox, helpString('intro.points.' + textId), { duration: 0 });
78020                 });
78021
78022                 context.on('enter.intro', function(mode) {
78023                     if (mode.id !== 'select') { return chapter.restart(); }
78024                     _pointID = context.mode().selectedIDs()[0];
78025                     continueTo(searchPreset);
78026                 });
78027
78028                 function continueTo(nextStep) {
78029                     context.map().on('move.intro drawn.intro', null);
78030                     context.on('enter.intro', null);
78031                     nextStep();
78032                 }
78033             }
78034
78035
78036             function searchPreset() {
78037                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78038                     return addPoint();
78039                 }
78040
78041                 // disallow scrolling
78042                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78043
78044                 context.container().select('.preset-search-input')
78045                     .on('keydown.intro', null)
78046                     .on('keyup.intro', checkPresetSearch);
78047
78048                 reveal('.preset-search-input',
78049                     helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78050                 );
78051
78052                 context.on('enter.intro', function(mode) {
78053                     if (!_pointID || !context.hasEntity(_pointID)) {
78054                         return continueTo(addPoint);
78055                     }
78056
78057                     var ids = context.selectedIDs();
78058                     if (mode.id !== 'select' || !ids.length || ids[0] !== _pointID) {
78059                         // keep the user's point selected..
78060                         context.enter(modeSelect(context, [_pointID]));
78061
78062                         // disallow scrolling
78063                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78064
78065                         context.container().select('.preset-search-input')
78066                             .on('keydown.intro', null)
78067                             .on('keyup.intro', checkPresetSearch);
78068
78069                         reveal('.preset-search-input',
78070                             helpString('intro.points.search_cafe', { preset: cafePreset.name() })
78071                         );
78072
78073                         context.history().on('change.intro', null);
78074                     }
78075                 });
78076
78077
78078                 function checkPresetSearch() {
78079                     var first = context.container().select('.preset-list-item:first-child');
78080
78081                     if (first.classed('preset-amenity-cafe')) {
78082                         context.container().select('.preset-search-input')
78083                             .on('keydown.intro', eventCancel, true)
78084                             .on('keyup.intro', null);
78085
78086                         reveal(first.select('.preset-list-button').node(),
78087                             helpString('intro.points.choose_cafe', { preset: cafePreset.name() }),
78088                             { duration: 300 }
78089                         );
78090
78091                         context.history().on('change.intro', function() {
78092                             continueTo(aboutFeatureEditor);
78093                         });
78094                     }
78095                 }
78096
78097                 function continueTo(nextStep) {
78098                     context.on('enter.intro', null);
78099                     context.history().on('change.intro', null);
78100                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78101                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78102                     nextStep();
78103                 }
78104             }
78105
78106
78107             function aboutFeatureEditor() {
78108                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78109                     return addPoint();
78110                 }
78111
78112                 timeout(function() {
78113                     reveal('.entity-editor-pane', helpString('intro.points.feature_editor'), {
78114                         tooltipClass: 'intro-points-describe',
78115                         buttonText: _t('intro.ok'),
78116                         buttonCallback: function() { continueTo(addName); }
78117                     });
78118                 }, 400);
78119
78120                 context.on('exit.intro', function() {
78121                     // if user leaves select mode here, just continue with the tutorial.
78122                     continueTo(reselectPoint);
78123                 });
78124
78125                 function continueTo(nextStep) {
78126                     context.on('exit.intro', null);
78127                     nextStep();
78128                 }
78129             }
78130
78131
78132             function addName() {
78133                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78134                     return addPoint();
78135                 }
78136
78137                 // reset pane, in case user happened to change it..
78138                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78139
78140                 var addNameString = helpString('intro.points.fields_info') + '{br}' + helpString('intro.points.add_name');
78141
78142                 timeout(function() {
78143                     // It's possible for the user to add a name in a previous step..
78144                     // If so, don't tell them to add the name in this step.
78145                     // Give them an OK button instead.
78146                     var entity = context.entity(_pointID);
78147                     if (entity.tags.name) {
78148                         var tooltip = reveal('.entity-editor-pane', addNameString, {
78149                             tooltipClass: 'intro-points-describe',
78150                             buttonText: _t('intro.ok'),
78151                             buttonCallback: function() { continueTo(addCloseEditor); }
78152                         });
78153                         tooltip.select('.instruction').style('display', 'none');
78154
78155                     } else {
78156                         reveal('.entity-editor-pane', addNameString,
78157                             { tooltipClass: 'intro-points-describe' }
78158                         );
78159                     }
78160                 }, 400);
78161
78162                 context.history().on('change.intro', function() {
78163                     continueTo(addCloseEditor);
78164                 });
78165
78166                 context.on('exit.intro', function() {
78167                     // if user leaves select mode here, just continue with the tutorial.
78168                     continueTo(reselectPoint);
78169                 });
78170
78171                 function continueTo(nextStep) {
78172                     context.on('exit.intro', null);
78173                     context.history().on('change.intro', null);
78174                     nextStep();
78175                 }
78176             }
78177
78178
78179             function addCloseEditor() {
78180                 // reset pane, in case user happened to change it..
78181                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78182
78183                 var selector = '.entity-editor-pane button.close svg use';
78184                 var href = select(selector).attr('href') || '#iD-icon-close';
78185
78186                 context.on('exit.intro', function() {
78187                     continueTo(reselectPoint);
78188                 });
78189
78190                 reveal('.entity-editor-pane',
78191                     helpString('intro.points.add_close', { button: icon(href, 'pre-text') })
78192                 );
78193
78194                 function continueTo(nextStep) {
78195                     context.on('exit.intro', null);
78196                     nextStep();
78197                 }
78198             }
78199
78200
78201             function reselectPoint() {
78202                 if (!_pointID) { return chapter.restart(); }
78203                 var entity = context.hasEntity(_pointID);
78204                 if (!entity) { return chapter.restart(); }
78205
78206                 // make sure it's still a cafe, in case user somehow changed it..
78207                 var oldPreset = _mainPresetIndex.match(entity, context.graph());
78208                 context.replace(actionChangePreset(_pointID, oldPreset, cafePreset));
78209
78210                 context.enter(modeBrowse(context));
78211
78212                 var msec = transitionTime(entity.loc, context.map().center());
78213                 if (msec) { reveal(null, null, { duration: 0 }); }
78214                 context.map().centerEase(entity.loc, msec);
78215
78216                 timeout(function() {
78217                     var box = pointBox(entity.loc, context);
78218                     reveal(box, helpString('intro.points.reselect'), { duration: 600 });
78219
78220                     timeout(function() {
78221                         context.map().on('move.intro drawn.intro', function() {
78222                             var entity = context.hasEntity(_pointID);
78223                             if (!entity) { return chapter.restart(); }
78224                             var box = pointBox(entity.loc, context);
78225                             reveal(box, helpString('intro.points.reselect'), { duration: 0 });
78226                         });
78227                     }, 600); // after reveal..
78228
78229                     context.on('enter.intro', function(mode) {
78230                         if (mode.id !== 'select') { return; }
78231                         continueTo(updatePoint);
78232                     });
78233
78234                 }, msec + 100);
78235
78236                 function continueTo(nextStep) {
78237                     context.map().on('move.intro drawn.intro', null);
78238                     context.on('enter.intro', null);
78239                     nextStep();
78240                 }
78241             }
78242
78243
78244             function updatePoint() {
78245                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78246                     return continueTo(reselectPoint);
78247                 }
78248
78249                 // reset pane, in case user happened to untag the point..
78250                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78251
78252                 context.on('exit.intro', function() {
78253                     continueTo(reselectPoint);
78254                 });
78255
78256                 context.history().on('change.intro', function() {
78257                     continueTo(updateCloseEditor);
78258                 });
78259
78260                 timeout(function() {
78261                     reveal('.entity-editor-pane', helpString('intro.points.update'),
78262                         { tooltipClass: 'intro-points-describe' }
78263                     );
78264                 }, 400);
78265
78266                 function continueTo(nextStep) {
78267                     context.on('exit.intro', null);
78268                     context.history().on('change.intro', null);
78269                     nextStep();
78270                 }
78271             }
78272
78273
78274             function updateCloseEditor() {
78275                 if (context.mode().id !== 'select' || !_pointID || !context.hasEntity(_pointID)) {
78276                     return continueTo(reselectPoint);
78277                 }
78278
78279                 // reset pane, in case user happened to change it..
78280                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78281
78282                 context.on('exit.intro', function() {
78283                     continueTo(rightClickPoint);
78284                 });
78285
78286                 timeout(function() {
78287                     reveal('.entity-editor-pane',
78288                         helpString('intro.points.update_close', { button: icon('#iD-icon-close', 'pre-text') })
78289                     );
78290                 }, 500);
78291
78292                 function continueTo(nextStep) {
78293                     context.on('exit.intro', null);
78294                     nextStep();
78295                 }
78296             }
78297
78298
78299             function rightClickPoint() {
78300                 if (!_pointID) { return chapter.restart(); }
78301                 var entity = context.hasEntity(_pointID);
78302                 if (!entity) { return chapter.restart(); }
78303
78304                 context.enter(modeBrowse(context));
78305
78306                 var box = pointBox(entity.loc, context);
78307                 var textId = context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch';
78308                 reveal(box, helpString('intro.points.' + textId), { duration: 600 });
78309
78310                 timeout(function() {
78311                     context.map().on('move.intro', function() {
78312                         var entity = context.hasEntity(_pointID);
78313                         if (!entity) { return chapter.restart(); }
78314                         var box = pointBox(entity.loc, context);
78315                         reveal(box, helpString('intro.points.' + textId), { duration: 0 });
78316                     });
78317                 }, 600); // after reveal
78318
78319                 context.on('enter.intro', function(mode) {
78320                     if (mode.id !== 'select') { return; }
78321                     var ids = context.selectedIDs();
78322                     if (ids.length !== 1 || ids[0] !== _pointID) { return; }
78323
78324                     timeout(function() {
78325                         var node = selectMenuItem(context, 'delete').node();
78326                         if (!node) { return; }
78327                         continueTo(enterDelete);
78328                     }, 50);  // after menu visible
78329                 });
78330
78331                 function continueTo(nextStep) {
78332                     context.on('enter.intro', null);
78333                     context.map().on('move.intro', null);
78334                     nextStep();
78335                 }
78336             }
78337
78338
78339             function enterDelete() {
78340                 if (!_pointID) { return chapter.restart(); }
78341                 var entity = context.hasEntity(_pointID);
78342                 if (!entity) { return chapter.restart(); }
78343
78344                 var node = selectMenuItem(context, 'delete').node();
78345                 if (!node) { return continueTo(rightClickPoint); }
78346
78347                 reveal('.edit-menu',
78348                     helpString('intro.points.delete'),
78349                     { padding: 50 }
78350                 );
78351
78352                 timeout(function() {
78353                     context.map().on('move.intro', function() {
78354                         reveal('.edit-menu',
78355                             helpString('intro.points.delete'),
78356                             { duration: 0,  padding: 50 }
78357                         );
78358                     });
78359                 }, 300); // after menu visible
78360
78361                 context.on('exit.intro', function() {
78362                     if (!_pointID) { return chapter.restart(); }
78363                     var entity = context.hasEntity(_pointID);
78364                     if (entity) { return continueTo(rightClickPoint); }  // point still exists
78365                 });
78366
78367                 context.history().on('change.intro', function(changed) {
78368                     if (changed.deleted().length) {
78369                         continueTo(undo);
78370                     }
78371                 });
78372
78373                 function continueTo(nextStep) {
78374                     context.map().on('move.intro', null);
78375                     context.history().on('change.intro', null);
78376                     context.on('exit.intro', null);
78377                     nextStep();
78378                 }
78379             }
78380
78381
78382             function undo() {
78383                 context.history().on('change.intro', function() {
78384                     continueTo(play);
78385                 });
78386
78387                 reveal('.top-toolbar button.undo-button',
78388                     helpString('intro.points.undo')
78389                 );
78390
78391                 function continueTo(nextStep) {
78392                     context.history().on('change.intro', null);
78393                     nextStep();
78394                 }
78395             }
78396
78397
78398             function play() {
78399                 dispatch$1.call('done');
78400                 reveal('.ideditor',
78401                     helpString('intro.points.play', { next: _t('intro.areas.title') }), {
78402                         tooltipBox: '.intro-nav-wrap .chapter-area',
78403                         buttonText: _t('intro.ok'),
78404                         buttonCallback: function() { reveal('.ideditor'); }
78405                     }
78406                 );
78407             }
78408
78409
78410             chapter.enter = function() {
78411                 addPoint();
78412             };
78413
78414
78415             chapter.exit = function() {
78416                 timeouts.forEach(window.clearTimeout);
78417                 context.on('enter.intro exit.intro', null);
78418                 context.map().on('move.intro drawn.intro', null);
78419                 context.history().on('change.intro', null);
78420                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78421                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78422             };
78423
78424
78425             chapter.restart = function() {
78426                 chapter.exit();
78427                 chapter.enter();
78428             };
78429
78430
78431             return utilRebind(chapter, dispatch$1, 'on');
78432         }
78433
78434         function uiIntroArea(context, reveal) {
78435             var dispatch$1 = dispatch('done');
78436             var playground = [-85.63552, 41.94159];
78437             var playgroundPreset = _mainPresetIndex.item('leisure/playground');
78438             var nameField = _mainPresetIndex.field('name');
78439             var descriptionField = _mainPresetIndex.field('description');
78440             var timeouts = [];
78441             var _areaID;
78442
78443
78444             var chapter = {
78445                 title: 'intro.areas.title'
78446             };
78447
78448
78449             function timeout(f, t) {
78450                 timeouts.push(window.setTimeout(f, t));
78451             }
78452
78453
78454             function eventCancel() {
78455                 event.stopPropagation();
78456                 event.preventDefault();
78457             }
78458
78459
78460             function revealPlayground(center, text, options) {
78461                 var padding = 180 * Math.pow(2, context.map().zoom() - 19.5);
78462                 var box = pad(center, padding, context);
78463                 reveal(box, text, options);
78464             }
78465
78466
78467             function addArea() {
78468                 context.enter(modeBrowse(context));
78469                 context.history().reset('initial');
78470                 _areaID = null;
78471
78472                 var msec = transitionTime(playground, context.map().center());
78473                 if (msec) { reveal(null, null, { duration: 0 }); }
78474                 context.map().centerZoomEase(playground, 19, msec);
78475
78476                 timeout(function() {
78477                     var tooltip = reveal('button.add-area',
78478                         helpString('intro.areas.add_playground'));
78479
78480                     tooltip.selectAll('.popover-inner')
78481                         .insert('svg', 'span')
78482                         .attr('class', 'tooltip-illustration')
78483                         .append('use')
78484                         .attr('xlink:href', '#iD-graphic-areas');
78485
78486                     context.on('enter.intro', function(mode) {
78487                         if (mode.id !== 'add-area') { return; }
78488                         continueTo(startPlayground);
78489                     });
78490                 }, msec + 100);
78491
78492                 function continueTo(nextStep) {
78493                     context.on('enter.intro', null);
78494                     nextStep();
78495                 }
78496             }
78497
78498
78499             function startPlayground() {
78500                 if (context.mode().id !== 'add-area') {
78501                     return chapter.restart();
78502                 }
78503
78504                 _areaID = null;
78505                 context.map().zoomEase(19.5, 500);
78506
78507                 timeout(function() {
78508                     var textId = context.lastPointerType() === 'mouse' ? 'starting_node_click' : 'starting_node_tap';
78509                     var startDrawString = helpString('intro.areas.start_playground') + helpString('intro.areas.' + textId);
78510                     revealPlayground(playground,
78511                         startDrawString, { duration: 250 }
78512                     );
78513
78514                     timeout(function() {
78515                         context.map().on('move.intro drawn.intro', function() {
78516                             revealPlayground(playground,
78517                                 startDrawString, { duration: 0 }
78518                             );
78519                         });
78520                         context.on('enter.intro', function(mode) {
78521                             if (mode.id !== 'draw-area') { return chapter.restart(); }
78522                             continueTo(continuePlayground);
78523                         });
78524                     }, 250);  // after reveal
78525
78526                 }, 550);  // after easing
78527
78528                 function continueTo(nextStep) {
78529                     context.map().on('move.intro drawn.intro', null);
78530                     context.on('enter.intro', null);
78531                     nextStep();
78532                 }
78533             }
78534
78535
78536             function continuePlayground() {
78537                 if (context.mode().id !== 'draw-area') {
78538                     return chapter.restart();
78539                 }
78540
78541                 _areaID = null;
78542                 revealPlayground(playground,
78543                     helpString('intro.areas.continue_playground'),
78544                     { duration: 250 }
78545                 );
78546
78547                 timeout(function() {
78548                     context.map().on('move.intro drawn.intro', function() {
78549                         revealPlayground(playground,
78550                             helpString('intro.areas.continue_playground'),
78551                             { duration: 0 }
78552                         );
78553                     });
78554                 }, 250);  // after reveal
78555
78556                 context.on('enter.intro', function(mode) {
78557                     if (mode.id === 'draw-area') {
78558                         var entity = context.hasEntity(context.selectedIDs()[0]);
78559                         if (entity && entity.nodes.length >= 6) {
78560                             return continueTo(finishPlayground);
78561                         } else {
78562                             return;
78563                         }
78564                     } else if (mode.id === 'select') {
78565                         _areaID = context.selectedIDs()[0];
78566                         return continueTo(searchPresets);
78567                     } else {
78568                         return chapter.restart();
78569                     }
78570                 });
78571
78572                 function continueTo(nextStep) {
78573                     context.map().on('move.intro drawn.intro', null);
78574                     context.on('enter.intro', null);
78575                     nextStep();
78576                 }
78577             }
78578
78579
78580             function finishPlayground() {
78581                 if (context.mode().id !== 'draw-area') {
78582                     return chapter.restart();
78583                 }
78584
78585                 _areaID = null;
78586
78587                 var finishString = helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
78588                     helpString('intro.areas.finish_playground');
78589                 revealPlayground(playground,
78590                     finishString, { duration: 250 }
78591                 );
78592
78593                 timeout(function() {
78594                     context.map().on('move.intro drawn.intro', function() {
78595                         revealPlayground(playground,
78596                             finishString, { duration: 0 }
78597                         );
78598                     });
78599                 }, 250);  // after reveal
78600
78601                 context.on('enter.intro', function(mode) {
78602                     if (mode.id === 'draw-area') {
78603                         return;
78604                     } else if (mode.id === 'select') {
78605                         _areaID = context.selectedIDs()[0];
78606                         return continueTo(searchPresets);
78607                     } else {
78608                         return chapter.restart();
78609                     }
78610                 });
78611
78612                 function continueTo(nextStep) {
78613                     context.map().on('move.intro drawn.intro', null);
78614                     context.on('enter.intro', null);
78615                     nextStep();
78616                 }
78617             }
78618
78619
78620             function searchPresets() {
78621                 if (!_areaID || !context.hasEntity(_areaID)) {
78622                     return addArea();
78623                 }
78624                 var ids = context.selectedIDs();
78625                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78626                     context.enter(modeSelect(context, [_areaID]));
78627                 }
78628
78629                 // disallow scrolling
78630                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78631
78632                 timeout(function() {
78633                     // reset pane, in case user somehow happened to change it..
78634                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78635
78636                     context.container().select('.preset-search-input')
78637                         .on('keydown.intro', null)
78638                         .on('keyup.intro', checkPresetSearch);
78639
78640                     reveal('.preset-search-input',
78641                         helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78642                     );
78643                 }, 400);  // after preset list pane visible..
78644
78645                 context.on('enter.intro', function(mode) {
78646                     if (!_areaID || !context.hasEntity(_areaID)) {
78647                         return continueTo(addArea);
78648                     }
78649
78650                     var ids = context.selectedIDs();
78651                     if (mode.id !== 'select' || !ids.length || ids[0] !== _areaID) {
78652                         // keep the user's area selected..
78653                         context.enter(modeSelect(context, [_areaID]));
78654
78655                         // reset pane, in case user somehow happened to change it..
78656                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
78657                         // disallow scrolling
78658                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78659
78660                         context.container().select('.preset-search-input')
78661                             .on('keydown.intro', null)
78662                             .on('keyup.intro', checkPresetSearch);
78663
78664                         reveal('.preset-search-input',
78665                             helpString('intro.areas.search_playground', { preset: playgroundPreset.name() })
78666                         );
78667
78668                         context.history().on('change.intro', null);
78669                     }
78670                 });
78671
78672                 function checkPresetSearch() {
78673                     var first = context.container().select('.preset-list-item:first-child');
78674
78675                     if (first.classed('preset-leisure-playground')) {
78676                         reveal(first.select('.preset-list-button').node(),
78677                             helpString('intro.areas.choose_playground', { preset: playgroundPreset.name() }),
78678                             { duration: 300 }
78679                         );
78680
78681                         context.container().select('.preset-search-input')
78682                             .on('keydown.intro', eventCancel, true)
78683                             .on('keyup.intro', null);
78684
78685                         context.history().on('change.intro', function() {
78686                             continueTo(clickAddField);
78687                         });
78688                     }
78689                 }
78690
78691                 function continueTo(nextStep) {
78692                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78693                     context.on('enter.intro', null);
78694                     context.history().on('change.intro', null);
78695                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78696                     nextStep();
78697                 }
78698             }
78699
78700
78701             function clickAddField() {
78702                 if (!_areaID || !context.hasEntity(_areaID)) {
78703                     return addArea();
78704                 }
78705                 var ids = context.selectedIDs();
78706                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78707                     return searchPresets();
78708                 }
78709
78710                 if (!context.container().select('.form-field-description').empty()) {
78711                     return continueTo(describePlayground);
78712                 }
78713
78714                 // disallow scrolling
78715                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
78716
78717                 timeout(function() {
78718                     // reset pane, in case user somehow happened to change it..
78719                     context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78720
78721                     // It's possible for the user to add a description in a previous step..
78722                     // If they did this already, just continue to next step.
78723                     var entity = context.entity(_areaID);
78724                     if (entity.tags.description) {
78725                         return continueTo(play);
78726                     }
78727
78728                     // scroll "Add field" into view
78729                     var box = context.container().select('.more-fields').node().getBoundingClientRect();
78730                     if (box.top > 300) {
78731                         var pane = context.container().select('.entity-editor-pane .inspector-body');
78732                         var start = pane.node().scrollTop;
78733                         var end = start + (box.top - 300);
78734
78735                         pane
78736                             .transition()
78737                             .duration(250)
78738                             .tween('scroll.inspector', function() {
78739                                 var node = this;
78740                                 var i = d3_interpolateNumber(start, end);
78741                                 return function(t) {
78742                                     node.scrollTop = i(t);
78743                                 };
78744                             });
78745                     }
78746
78747                     timeout(function() {
78748                         reveal('.more-fields .combobox-input',
78749                             helpString('intro.areas.add_field', {
78750                                 name: nameField.label(),
78751                                 description: descriptionField.label()
78752                             }),
78753                             { duration: 300 }
78754                         );
78755
78756                         context.container().select('.more-fields .combobox-input')
78757                             .on('click.intro', function() {
78758                                 // Watch for the combobox to appear...
78759                                 var watcher;
78760                                 watcher = window.setInterval(function() {
78761                                     if (!context.container().select('div.combobox').empty()) {
78762                                         window.clearInterval(watcher);
78763                                         continueTo(chooseDescriptionField);
78764                                     }
78765                                 }, 300);
78766                             });
78767                     }, 300);  // after "Add Field" visible
78768
78769                 }, 400);  // after editor pane visible
78770
78771                 context.on('exit.intro', function() {
78772                     return continueTo(searchPresets);
78773                 });
78774
78775                 function continueTo(nextStep) {
78776                     context.container().select('.inspector-wrap').on('wheel.intro', null);
78777                     context.container().select('.more-fields .combobox-input').on('click.intro', null);
78778                     context.on('exit.intro', null);
78779                     nextStep();
78780                 }
78781             }
78782
78783
78784             function chooseDescriptionField() {
78785                 if (!_areaID || !context.hasEntity(_areaID)) {
78786                     return addArea();
78787                 }
78788                 var ids = context.selectedIDs();
78789                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78790                     return searchPresets();
78791                 }
78792
78793                 if (!context.container().select('.form-field-description').empty()) {
78794                     return continueTo(describePlayground);
78795                 }
78796
78797                 // Make sure combobox is ready..
78798                 if (context.container().select('div.combobox').empty()) {
78799                     return continueTo(clickAddField);
78800                 }
78801                 // Watch for the combobox to go away..
78802                 var watcher;
78803                 watcher = window.setInterval(function() {
78804                     if (context.container().select('div.combobox').empty()) {
78805                         window.clearInterval(watcher);
78806                         timeout(function() {
78807                             if (context.container().select('.form-field-description').empty()) {
78808                                 continueTo(retryChooseDescription);
78809                             } else {
78810                                 continueTo(describePlayground);
78811                             }
78812                         }, 300);  // after description field added.
78813                     }
78814                 }, 300);
78815
78816                 reveal('div.combobox',
78817                     helpString('intro.areas.choose_field', { field: descriptionField.label() }),
78818                     { duration: 300 }
78819                 );
78820
78821                 context.on('exit.intro', function() {
78822                     return continueTo(searchPresets);
78823                 });
78824
78825                 function continueTo(nextStep) {
78826                     if (watcher) { window.clearInterval(watcher); }
78827                     context.on('exit.intro', null);
78828                     nextStep();
78829                 }
78830             }
78831
78832
78833             function describePlayground() {
78834                 if (!_areaID || !context.hasEntity(_areaID)) {
78835                     return addArea();
78836                 }
78837                 var ids = context.selectedIDs();
78838                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78839                     return searchPresets();
78840                 }
78841
78842                 // reset pane, in case user happened to change it..
78843                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78844
78845                 if (context.container().select('.form-field-description').empty()) {
78846                     return continueTo(retryChooseDescription);
78847                 }
78848
78849                 context.on('exit.intro', function() {
78850                     continueTo(play);
78851                 });
78852
78853                 reveal('.entity-editor-pane',
78854                     helpString('intro.areas.describe_playground', { button: icon('#iD-icon-close', 'pre-text') }),
78855                     { duration: 300 }
78856                 );
78857
78858                 function continueTo(nextStep) {
78859                     context.on('exit.intro', null);
78860                     nextStep();
78861                 }
78862             }
78863
78864
78865             function retryChooseDescription() {
78866                 if (!_areaID || !context.hasEntity(_areaID)) {
78867                     return addArea();
78868                 }
78869                 var ids = context.selectedIDs();
78870                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _areaID) {
78871                     return searchPresets();
78872                 }
78873
78874                 // reset pane, in case user happened to change it..
78875                 context.container().select('.inspector-wrap .panewrap').style('right', '0%');
78876
78877                 reveal('.entity-editor-pane',
78878                     helpString('intro.areas.retry_add_field', { field: descriptionField.label() }), {
78879                     buttonText: _t('intro.ok'),
78880                     buttonCallback: function() { continueTo(clickAddField); }
78881                 });
78882
78883                 context.on('exit.intro', function() {
78884                     return continueTo(searchPresets);
78885                 });
78886
78887                 function continueTo(nextStep) {
78888                     context.on('exit.intro', null);
78889                     nextStep();
78890                 }
78891             }
78892
78893
78894             function play() {
78895                 dispatch$1.call('done');
78896                 reveal('.ideditor',
78897                     helpString('intro.areas.play', { next: _t('intro.lines.title') }), {
78898                         tooltipBox: '.intro-nav-wrap .chapter-line',
78899                         buttonText: _t('intro.ok'),
78900                         buttonCallback: function() { reveal('.ideditor'); }
78901                     }
78902                 );
78903             }
78904
78905
78906             chapter.enter = function() {
78907                 addArea();
78908             };
78909
78910
78911             chapter.exit = function() {
78912                 timeouts.forEach(window.clearTimeout);
78913                 context.on('enter.intro exit.intro', null);
78914                 context.map().on('move.intro drawn.intro', null);
78915                 context.history().on('change.intro', null);
78916                 context.container().select('.inspector-wrap').on('wheel.intro', null);
78917                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
78918                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
78919             };
78920
78921
78922             chapter.restart = function() {
78923                 chapter.exit();
78924                 chapter.enter();
78925             };
78926
78927
78928             return utilRebind(chapter, dispatch$1, 'on');
78929         }
78930
78931         function uiIntroLine(context, reveal) {
78932             var dispatch$1 = dispatch('done');
78933             var timeouts = [];
78934             var _tulipRoadID = null;
78935             var flowerRoadID = 'w646';
78936             var tulipRoadStart = [-85.6297754121684, 41.95805253325314];
78937             var tulipRoadMidpoint = [-85.62975395449628, 41.95787501510204];
78938             var tulipRoadIntersection = [-85.62974496187628, 41.95742515554585];
78939             var roadCategory = _mainPresetIndex.item('category-road_minor');
78940             var residentialPreset = _mainPresetIndex.item('highway/residential');
78941             var woodRoadID = 'w525';
78942             var woodRoadEndID = 'n2862';
78943             var woodRoadAddNode = [-85.62390110349587, 41.95397111462291];
78944             var woodRoadDragEndpoint = [-85.623867390213, 41.95466987786487];
78945             var woodRoadDragMidpoint = [-85.62386254803509, 41.95430395953872];
78946             var washingtonStreetID = 'w522';
78947             var twelfthAvenueID = 'w1';
78948             var eleventhAvenueEndID = 'n3550';
78949             var twelfthAvenueEndID = 'n5';
78950             var _washingtonSegmentID = null;
78951             var eleventhAvenueEnd = context.entity(eleventhAvenueEndID).loc;
78952             var twelfthAvenueEnd = context.entity(twelfthAvenueEndID).loc;
78953             var deleteLinesLoc = [-85.6219395542764, 41.95228033922477];
78954             var twelfthAvenue = [-85.62219310052491, 41.952505413152956];
78955
78956
78957             var chapter = {
78958                 title: 'intro.lines.title'
78959             };
78960
78961
78962             function timeout(f, t) {
78963                 timeouts.push(window.setTimeout(f, t));
78964             }
78965
78966
78967             function eventCancel() {
78968                 event.stopPropagation();
78969                 event.preventDefault();
78970             }
78971
78972
78973             function addLine() {
78974                 context.enter(modeBrowse(context));
78975                 context.history().reset('initial');
78976
78977                 var msec = transitionTime(tulipRoadStart, context.map().center());
78978                 if (msec) { reveal(null, null, { duration: 0 }); }
78979                 context.map().centerZoomEase(tulipRoadStart, 18.5, msec);
78980
78981                 timeout(function() {
78982                     var tooltip = reveal('button.add-line',
78983                         helpString('intro.lines.add_line'));
78984
78985                     tooltip.selectAll('.popover-inner')
78986                         .insert('svg', 'span')
78987                         .attr('class', 'tooltip-illustration')
78988                         .append('use')
78989                         .attr('xlink:href', '#iD-graphic-lines');
78990
78991                     context.on('enter.intro', function(mode) {
78992                         if (mode.id !== 'add-line') { return; }
78993                         continueTo(startLine);
78994                     });
78995                 }, msec + 100);
78996
78997                 function continueTo(nextStep) {
78998                     context.on('enter.intro', null);
78999                     nextStep();
79000                 }
79001             }
79002
79003
79004             function startLine() {
79005                 if (context.mode().id !== 'add-line') { return chapter.restart(); }
79006
79007                 _tulipRoadID = null;
79008
79009                 var padding = 70 * Math.pow(2, context.map().zoom() - 18);
79010                 var box = pad(tulipRoadStart, padding, context);
79011                 box.height = box.height + 100;
79012
79013                 var textId = context.lastPointerType() === 'mouse' ? 'start_line' : 'start_line_tap';
79014                 var startLineString = helpString('intro.lines.missing_road') + '{br}' +
79015                     helpString('intro.lines.line_draw_info') +
79016                     helpString('intro.lines.' + textId);
79017                 reveal(box, startLineString);
79018
79019                 context.map().on('move.intro drawn.intro', function() {
79020                     padding = 70 * Math.pow(2, context.map().zoom() - 18);
79021                     box = pad(tulipRoadStart, padding, context);
79022                     box.height = box.height + 100;
79023                     reveal(box, startLineString, { duration: 0 });
79024                 });
79025
79026                 context.on('enter.intro', function(mode) {
79027                     if (mode.id !== 'draw-line') { return chapter.restart(); }
79028                     continueTo(drawLine);
79029                 });
79030
79031                 function continueTo(nextStep) {
79032                     context.map().on('move.intro drawn.intro', null);
79033                     context.on('enter.intro', null);
79034                     nextStep();
79035                 }
79036             }
79037
79038
79039             function drawLine() {
79040                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79041
79042                 _tulipRoadID = context.mode().selectedIDs()[0];
79043                 context.map().centerEase(tulipRoadMidpoint, 500);
79044
79045                 timeout(function() {
79046                     var padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79047                     var box = pad(tulipRoadMidpoint, padding, context);
79048                     box.height = box.height * 2;
79049                     reveal(box,
79050                         helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') })
79051                     );
79052
79053                     context.map().on('move.intro drawn.intro', function() {
79054                         padding = 200 * Math.pow(2, context.map().zoom() - 18.5);
79055                         box = pad(tulipRoadMidpoint, padding, context);
79056                         box.height = box.height * 2;
79057                         reveal(box,
79058                             helpString('intro.lines.intersect', { name: _t('intro.graph.name.flower-street') }),
79059                             { duration: 0 }
79060                         );
79061                     });
79062                 }, 550);  // after easing..
79063
79064                 context.history().on('change.intro', function() {
79065                     if (isLineConnected()) {
79066                         continueTo(continueLine);
79067                     }
79068                 });
79069
79070                 context.on('enter.intro', function(mode) {
79071                     if (mode.id === 'draw-line') {
79072                         return;
79073                     } else if (mode.id === 'select') {
79074                         continueTo(retryIntersect);
79075                         return;
79076                     } else {
79077                         return chapter.restart();
79078                     }
79079                 });
79080
79081                 function continueTo(nextStep) {
79082                     context.map().on('move.intro drawn.intro', null);
79083                     context.history().on('change.intro', null);
79084                     context.on('enter.intro', null);
79085                     nextStep();
79086                 }
79087             }
79088
79089
79090             function isLineConnected() {
79091                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79092                 if (!entity) { return false; }
79093
79094                 var drawNodes = context.graph().childNodes(entity);
79095                 return drawNodes.some(function(node) {
79096                     return context.graph().parentWays(node).some(function(parent) {
79097                         return parent.id === flowerRoadID;
79098                     });
79099                 });
79100             }
79101
79102
79103             function retryIntersect() {
79104                 select(window).on('pointerdown.intro mousedown.intro', eventCancel, true);
79105
79106                 var box = pad(tulipRoadIntersection, 80, context);
79107                 reveal(box,
79108                     helpString('intro.lines.retry_intersect', { name: _t('intro.graph.name.flower-street') })
79109                 );
79110
79111                 timeout(chapter.restart, 3000);
79112             }
79113
79114
79115             function continueLine() {
79116                 if (context.mode().id !== 'draw-line') { return chapter.restart(); }
79117                 var entity = _tulipRoadID && context.hasEntity(_tulipRoadID);
79118                 if (!entity) { return chapter.restart(); }
79119
79120                 context.map().centerEase(tulipRoadIntersection, 500);
79121
79122                 var continueLineText = helpString('intro.lines.continue_line') + '{br}' +
79123                     helpString('intro.lines.finish_line_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
79124                     helpString('intro.lines.finish_road');
79125
79126                 reveal('.surface', continueLineText);
79127
79128                 context.on('enter.intro', function(mode) {
79129                     if (mode.id === 'draw-line')
79130                         { return; }
79131                     else if (mode.id === 'select')
79132                         { return continueTo(chooseCategoryRoad); }
79133                     else
79134                         { return chapter.restart(); }
79135                 });
79136
79137                 function continueTo(nextStep) {
79138                     context.on('enter.intro', null);
79139                     nextStep();
79140                 }
79141             }
79142
79143
79144             function chooseCategoryRoad() {
79145                 if (context.mode().id !== 'select') { return chapter.restart(); }
79146
79147                 context.on('exit.intro', function() {
79148                     return chapter.restart();
79149                 });
79150
79151                 var button = context.container().select('.preset-category-road_minor .preset-list-button');
79152                 if (button.empty()) { return chapter.restart(); }
79153
79154                 // disallow scrolling
79155                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79156
79157                 timeout(function() {
79158                     // reset pane, in case user somehow happened to change it..
79159                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
79160
79161                     reveal(button.node(),
79162                         helpString('intro.lines.choose_category_road', { category: roadCategory.name() })
79163                     );
79164
79165                     button.on('click.intro', function() {
79166                         continueTo(choosePresetResidential);
79167                     });
79168
79169                 }, 400);  // after editor pane visible
79170
79171                 function continueTo(nextStep) {
79172                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79173                     context.container().select('.preset-list-button').on('click.intro', null);
79174                     context.on('exit.intro', null);
79175                     nextStep();
79176                 }
79177             }
79178
79179
79180             function choosePresetResidential() {
79181                 if (context.mode().id !== 'select') { return chapter.restart(); }
79182
79183                 context.on('exit.intro', function() {
79184                     return chapter.restart();
79185                 });
79186
79187                 var subgrid = context.container().select('.preset-category-road_minor .subgrid');
79188                 if (subgrid.empty()) { return chapter.restart(); }
79189
79190                 subgrid.selectAll(':not(.preset-highway-residential) .preset-list-button')
79191                     .on('click.intro', function() {
79192                         continueTo(retryPresetResidential);
79193                     });
79194
79195                 subgrid.selectAll('.preset-highway-residential .preset-list-button')
79196                     .on('click.intro', function() {
79197                         continueTo(nameRoad);
79198                     });
79199
79200                 timeout(function() {
79201                     reveal(subgrid.node(),
79202                         helpString('intro.lines.choose_preset_residential', { preset: residentialPreset.name() }),
79203                         { tooltipBox: '.preset-highway-residential .preset-list-button', duration: 300 }
79204                     );
79205                 }, 300);
79206
79207                 function continueTo(nextStep) {
79208                     context.container().select('.preset-list-button').on('click.intro', null);
79209                     context.on('exit.intro', null);
79210                     nextStep();
79211                 }
79212             }
79213
79214
79215             // selected wrong road type
79216             function retryPresetResidential() {
79217                 if (context.mode().id !== 'select') { return chapter.restart(); }
79218
79219                 context.on('exit.intro', function() {
79220                     return chapter.restart();
79221                 });
79222
79223                 // disallow scrolling
79224                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
79225
79226                 timeout(function() {
79227                     var button = context.container().select('.entity-editor-pane .preset-list-button');
79228
79229                     reveal(button.node(),
79230                         helpString('intro.lines.retry_preset_residential', { preset: residentialPreset.name() })
79231                     );
79232
79233                     button.on('click.intro', function() {
79234                         continueTo(chooseCategoryRoad);
79235                     });
79236
79237                 }, 500);
79238
79239                 function continueTo(nextStep) {
79240                     context.container().select('.inspector-wrap').on('wheel.intro', null);
79241                     context.container().select('.preset-list-button').on('click.intro', null);
79242                     context.on('exit.intro', null);
79243                     nextStep();
79244                 }
79245             }
79246
79247
79248             function nameRoad() {
79249                 context.on('exit.intro', function() {
79250                     continueTo(didNameRoad);
79251                 });
79252
79253                 timeout(function() {
79254                     reveal('.entity-editor-pane',
79255                         helpString('intro.lines.name_road', { button: icon('#iD-icon-close', 'pre-text') }),
79256                         { tooltipClass: 'intro-lines-name_road' }
79257                     );
79258                 }, 500);
79259
79260                 function continueTo(nextStep) {
79261                     context.on('exit.intro', null);
79262                     nextStep();
79263                 }
79264             }
79265
79266
79267             function didNameRoad() {
79268                 context.history().checkpoint('doneAddLine');
79269
79270                 timeout(function() {
79271                     reveal('.surface', helpString('intro.lines.did_name_road'), {
79272                         buttonText: _t('intro.ok'),
79273                         buttonCallback: function() { continueTo(updateLine); }
79274                     });
79275                 }, 500);
79276
79277                 function continueTo(nextStep) {
79278                     nextStep();
79279                 }
79280             }
79281
79282
79283             function updateLine() {
79284                 context.history().reset('doneAddLine');
79285                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79286                     return chapter.restart();
79287                 }
79288
79289                 var msec = transitionTime(woodRoadDragMidpoint, context.map().center());
79290                 if (msec) { reveal(null, null, { duration: 0 }); }
79291                 context.map().centerZoomEase(woodRoadDragMidpoint, 19, msec);
79292
79293                 timeout(function() {
79294                     var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79295                     var box = pad(woodRoadDragMidpoint, padding, context);
79296                     var advance = function() { continueTo(addNode); };
79297
79298                     reveal(box, helpString('intro.lines.update_line'),
79299                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79300                     );
79301
79302                     context.map().on('move.intro drawn.intro', function() {
79303                         var padding = 250 * Math.pow(2, context.map().zoom() - 19);
79304                         var box = pad(woodRoadDragMidpoint, padding, context);
79305                         reveal(box, helpString('intro.lines.update_line'),
79306                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79307                         );
79308                     });
79309                 }, msec + 100);
79310
79311                 function continueTo(nextStep) {
79312                     context.map().on('move.intro drawn.intro', null);
79313                     nextStep();
79314                 }
79315             }
79316
79317
79318             function addNode() {
79319                 context.history().reset('doneAddLine');
79320                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79321                     return chapter.restart();
79322                 }
79323
79324                 var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79325                 var box = pad(woodRoadAddNode, padding, context);
79326                 var addNodeString = helpString('intro.lines.add_node' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79327                 reveal(box, addNodeString);
79328
79329                 context.map().on('move.intro drawn.intro', function() {
79330                     var padding = 40 * Math.pow(2, context.map().zoom() - 19);
79331                     var box = pad(woodRoadAddNode, padding, context);
79332                     reveal(box, addNodeString, { duration: 0 });
79333                 });
79334
79335                 context.history().on('change.intro', function(changed) {
79336                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79337                         return continueTo(updateLine);
79338                     }
79339                     if (changed.created().length === 1) {
79340                         timeout(function() { continueTo(startDragEndpoint); }, 500);
79341                     }
79342                 });
79343
79344                 context.on('enter.intro', function(mode) {
79345                     if (mode.id !== 'select') {
79346                         continueTo(updateLine);
79347                     }
79348                 });
79349
79350                 function continueTo(nextStep) {
79351                     context.map().on('move.intro drawn.intro', null);
79352                     context.history().on('change.intro', null);
79353                     context.on('enter.intro', null);
79354                     nextStep();
79355                 }
79356             }
79357
79358
79359             function startDragEndpoint() {
79360                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79361                     return continueTo(updateLine);
79362                 }
79363                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79364                 var box = pad(woodRoadDragEndpoint, padding, context);
79365                 var startDragString = helpString('intro.lines.start_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch')) +
79366                     helpString('intro.lines.drag_to_intersection');
79367                 reveal(box, startDragString);
79368
79369                 context.map().on('move.intro drawn.intro', function() {
79370                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79371                         return continueTo(updateLine);
79372                     }
79373                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79374                     var box = pad(woodRoadDragEndpoint, padding, context);
79375                     reveal(box, startDragString, { duration: 0 });
79376
79377                     var entity = context.entity(woodRoadEndID);
79378                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) <= 4) {
79379                         continueTo(finishDragEndpoint);
79380                     }
79381                 });
79382
79383                 function continueTo(nextStep) {
79384                     context.map().on('move.intro drawn.intro', null);
79385                     nextStep();
79386                 }
79387             }
79388
79389
79390             function finishDragEndpoint() {
79391                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79392                     return continueTo(updateLine);
79393                 }
79394
79395                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79396                 var box = pad(woodRoadDragEndpoint, padding, context);
79397                 var finishDragString = helpString('intro.lines.spot_looks_good') +
79398                     helpString('intro.lines.finish_drag_endpoint' + (context.lastPointerType() === 'mouse' ? '' : '_touch'));
79399                 reveal(box, finishDragString);
79400
79401                 context.map().on('move.intro drawn.intro', function() {
79402                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79403                         return continueTo(updateLine);
79404                     }
79405                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79406                     var box = pad(woodRoadDragEndpoint, padding, context);
79407                     reveal(box, finishDragString, { duration: 0 });
79408
79409                     var entity = context.entity(woodRoadEndID);
79410                     if (geoSphericalDistance(entity.loc, woodRoadDragEndpoint) > 4) {
79411                         continueTo(startDragEndpoint);
79412                     }
79413                 });
79414
79415                 context.on('enter.intro', function() {
79416                     continueTo(startDragMidpoint);
79417                 });
79418
79419                 function continueTo(nextStep) {
79420                     context.map().on('move.intro drawn.intro', null);
79421                     context.on('enter.intro', null);
79422                     nextStep();
79423                 }
79424             }
79425
79426
79427             function startDragMidpoint() {
79428                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79429                     return continueTo(updateLine);
79430                 }
79431                 if (context.selectedIDs().indexOf(woodRoadID) === -1) {
79432                     context.enter(modeSelect(context, [woodRoadID]));
79433                 }
79434
79435                 var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79436                 var box = pad(woodRoadDragMidpoint, padding, context);
79437                 reveal(box, helpString('intro.lines.start_drag_midpoint'));
79438
79439                 context.map().on('move.intro drawn.intro', function() {
79440                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79441                         return continueTo(updateLine);
79442                     }
79443                     var padding = 80 * Math.pow(2, context.map().zoom() - 19);
79444                     var box = pad(woodRoadDragMidpoint, padding, context);
79445                     reveal(box, helpString('intro.lines.start_drag_midpoint'), { duration: 0 });
79446                 });
79447
79448                 context.history().on('change.intro', function(changed) {
79449                     if (changed.created().length === 1) {
79450                         continueTo(continueDragMidpoint);
79451                     }
79452                 });
79453
79454                 context.on('enter.intro', function(mode) {
79455                     if (mode.id !== 'select') {
79456                         // keep Wood Road selected so midpoint triangles are drawn..
79457                         context.enter(modeSelect(context, [woodRoadID]));
79458                     }
79459                 });
79460
79461                 function continueTo(nextStep) {
79462                     context.map().on('move.intro drawn.intro', null);
79463                     context.history().on('change.intro', null);
79464                     context.on('enter.intro', null);
79465                     nextStep();
79466                 }
79467             }
79468
79469
79470             function continueDragMidpoint() {
79471                 if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79472                     return continueTo(updateLine);
79473                 }
79474
79475                 var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79476                 var box = pad(woodRoadDragEndpoint, padding, context);
79477                 box.height += 400;
79478
79479                 var advance = function() {
79480                     context.history().checkpoint('doneUpdateLine');
79481                     continueTo(deleteLines);
79482                 };
79483
79484                 reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79485                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79486                 );
79487
79488                 context.map().on('move.intro drawn.intro', function() {
79489                     if (!context.hasEntity(woodRoadID) || !context.hasEntity(woodRoadEndID)) {
79490                         return continueTo(updateLine);
79491                     }
79492                     var padding = 100 * Math.pow(2, context.map().zoom() - 19);
79493                     var box = pad(woodRoadDragEndpoint, padding, context);
79494                     box.height += 400;
79495                     reveal(box, helpString('intro.lines.continue_drag_midpoint'),
79496                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79497                     );
79498                 });
79499
79500                 function continueTo(nextStep) {
79501                     context.map().on('move.intro drawn.intro', null);
79502                     nextStep();
79503                 }
79504             }
79505
79506
79507             function deleteLines() {
79508                 context.history().reset('doneUpdateLine');
79509                 context.enter(modeBrowse(context));
79510
79511                 if (!context.hasEntity(washingtonStreetID) ||
79512                     !context.hasEntity(twelfthAvenueID) ||
79513                     !context.hasEntity(eleventhAvenueEndID)) {
79514                     return chapter.restart();
79515                 }
79516
79517                 var msec = transitionTime(deleteLinesLoc, context.map().center());
79518                 if (msec) { reveal(null, null, { duration: 0 }); }
79519                 context.map().centerZoomEase(deleteLinesLoc, 18, msec);
79520
79521                 timeout(function() {
79522                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79523                     var box = pad(deleteLinesLoc, padding, context);
79524                     box.top -= 200;
79525                     box.height += 400;
79526                     var advance = function() { continueTo(rightClickIntersection); };
79527
79528                     reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79529                         { buttonText: _t('intro.ok'), buttonCallback: advance }
79530                     );
79531
79532                     context.map().on('move.intro drawn.intro', function() {
79533                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79534                         var box = pad(deleteLinesLoc, padding, context);
79535                         box.top -= 200;
79536                         box.height += 400;
79537                         reveal(box, helpString('intro.lines.delete_lines', { street: _t('intro.graph.name.12th-avenue') }),
79538                             { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79539                         );
79540                     });
79541
79542                     context.history().on('change.intro', function() {
79543                         timeout(function() {
79544                             continueTo(deleteLines);
79545                         }, 500);  // after any transition (e.g. if user deleted intersection)
79546                     });
79547
79548                 }, msec + 100);
79549
79550                 function continueTo(nextStep) {
79551                     context.map().on('move.intro drawn.intro', null);
79552                     context.history().on('change.intro', null);
79553                     nextStep();
79554                 }
79555             }
79556
79557
79558             function rightClickIntersection() {
79559                 context.history().reset('doneUpdateLine');
79560                 context.enter(modeBrowse(context));
79561
79562                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79563
79564                 var rightClickString = helpString('intro.lines.split_street', {
79565                         street1: _t('intro.graph.name.11th-avenue'),
79566                         street2: _t('intro.graph.name.washington-street')
79567                     }) +
79568                     helpString('intro.lines.' + (context.lastPointerType() === 'mouse' ? 'rightclick_intersection' : 'edit_menu_intersection_touch'));
79569
79570                 timeout(function() {
79571                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79572                     var box = pad(eleventhAvenueEnd, padding, context);
79573                     reveal(box, rightClickString);
79574
79575                     context.map().on('move.intro drawn.intro', function() {
79576                         var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79577                         var box = pad(eleventhAvenueEnd, padding, context);
79578                         reveal(box, rightClickString,
79579                             { duration: 0 }
79580                         );
79581                     });
79582
79583                     context.on('enter.intro', function(mode) {
79584                         if (mode.id !== 'select') { return; }
79585                         var ids = context.selectedIDs();
79586                         if (ids.length !== 1 || ids[0] !== eleventhAvenueEndID) { return; }
79587
79588                         timeout(function() {
79589                             var node = selectMenuItem(context, 'split').node();
79590                             if (!node) { return; }
79591                             continueTo(splitIntersection);
79592                         }, 50);  // after menu visible
79593                     });
79594
79595                     context.history().on('change.intro', function() {
79596                         timeout(function() {
79597                             continueTo(deleteLines);
79598                         }, 300);  // after any transition (e.g. if user deleted intersection)
79599                     });
79600
79601                 }, 600);
79602
79603                 function continueTo(nextStep) {
79604                     context.map().on('move.intro drawn.intro', null);
79605                     context.on('enter.intro', null);
79606                     context.history().on('change.intro', null);
79607                     nextStep();
79608                 }
79609             }
79610
79611
79612             function splitIntersection() {
79613                 if (!context.hasEntity(washingtonStreetID) ||
79614                     !context.hasEntity(twelfthAvenueID) ||
79615                     !context.hasEntity(eleventhAvenueEndID)) {
79616                     return continueTo(deleteLines);
79617                 }
79618
79619                 var node = selectMenuItem(context, 'split').node();
79620                 if (!node) { return continueTo(rightClickIntersection); }
79621
79622                 var wasChanged = false;
79623                 _washingtonSegmentID = null;
79624
79625                 reveal('.edit-menu', helpString('intro.lines.split_intersection',
79626                     { street: _t('intro.graph.name.washington-street') }),
79627                     { padding: 50 }
79628                 );
79629
79630                 context.map().on('move.intro drawn.intro', function() {
79631                     var node = selectMenuItem(context, 'split').node();
79632                     if (!wasChanged && !node) { return continueTo(rightClickIntersection); }
79633
79634                     reveal('.edit-menu', helpString('intro.lines.split_intersection',
79635                         { street: _t('intro.graph.name.washington-street') }),
79636                         { duration: 0, padding: 50 }
79637                     );
79638                 });
79639
79640                 context.history().on('change.intro', function(changed) {
79641                     wasChanged = true;
79642                     timeout(function() {
79643                         if (context.history().undoAnnotation() === _t('operations.split.annotation.line')) {
79644                             _washingtonSegmentID = changed.created()[0].id;
79645                             continueTo(didSplit);
79646                         } else {
79647                             _washingtonSegmentID = null;
79648                             continueTo(retrySplit);
79649                         }
79650                     }, 300);  // after any transition (e.g. if user deleted intersection)
79651                 });
79652
79653                 function continueTo(nextStep) {
79654                     context.map().on('move.intro drawn.intro', null);
79655                     context.history().on('change.intro', null);
79656                     nextStep();
79657                 }
79658             }
79659
79660
79661             function retrySplit() {
79662                 context.enter(modeBrowse(context));
79663                 context.map().centerZoomEase(eleventhAvenueEnd, 18, 500);
79664                 var advance = function() { continueTo(rightClickIntersection); };
79665
79666                 var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79667                 var box = pad(eleventhAvenueEnd, padding, context);
79668                 reveal(box, helpString('intro.lines.retry_split'),
79669                     { buttonText: _t('intro.ok'), buttonCallback: advance }
79670                 );
79671
79672                 context.map().on('move.intro drawn.intro', function() {
79673                     var padding = 60 * Math.pow(2, context.map().zoom() - 18);
79674                     var box = pad(eleventhAvenueEnd, padding, context);
79675                     reveal(box, helpString('intro.lines.retry_split'),
79676                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: advance }
79677                     );
79678                 });
79679
79680                 function continueTo(nextStep) {
79681                     context.map().on('move.intro drawn.intro', null);
79682                     nextStep();
79683                 }
79684             }
79685
79686
79687             function didSplit() {
79688                 if (!_washingtonSegmentID ||
79689                     !context.hasEntity(_washingtonSegmentID) ||
79690                     !context.hasEntity(washingtonStreetID) ||
79691                     !context.hasEntity(twelfthAvenueID) ||
79692                     !context.hasEntity(eleventhAvenueEndID)) {
79693                     return continueTo(rightClickIntersection);
79694                 }
79695
79696                 var ids = context.selectedIDs();
79697                 var string = 'intro.lines.did_split_' + (ids.length > 1 ? 'multi' : 'single');
79698                 var street = _t('intro.graph.name.washington-street');
79699
79700                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79701                 var box = pad(twelfthAvenue, padding, context);
79702                 box.width = box.width / 2;
79703                 reveal(box, helpString(string, { street1: street, street2: street }),
79704                     { duration: 500 }
79705                 );
79706
79707                 timeout(function() {
79708                     context.map().centerZoomEase(twelfthAvenue, 18, 500);
79709
79710                     context.map().on('move.intro drawn.intro', function() {
79711                         var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79712                         var box = pad(twelfthAvenue, padding, context);
79713                         box.width = box.width / 2;
79714                         reveal(box, helpString(string, { street1: street, street2: street }),
79715                             { duration: 0 }
79716                         );
79717                     });
79718                 }, 600);  // after initial reveal and curtain cut
79719
79720                 context.on('enter.intro', function() {
79721                     var ids = context.selectedIDs();
79722                     if (ids.length === 1 && ids[0] === _washingtonSegmentID) {
79723                         continueTo(multiSelect);
79724                     }
79725                 });
79726
79727                 context.history().on('change.intro', function() {
79728                     if (!_washingtonSegmentID ||
79729                         !context.hasEntity(_washingtonSegmentID) ||
79730                         !context.hasEntity(washingtonStreetID) ||
79731                         !context.hasEntity(twelfthAvenueID) ||
79732                         !context.hasEntity(eleventhAvenueEndID)) {
79733                         return continueTo(rightClickIntersection);
79734                     }
79735                 });
79736
79737                 function continueTo(nextStep) {
79738                     context.map().on('move.intro drawn.intro', null);
79739                     context.on('enter.intro', null);
79740                     context.history().on('change.intro', null);
79741                     nextStep();
79742                 }
79743             }
79744
79745
79746             function multiSelect() {
79747                 if (!_washingtonSegmentID ||
79748                     !context.hasEntity(_washingtonSegmentID) ||
79749                     !context.hasEntity(washingtonStreetID) ||
79750                     !context.hasEntity(twelfthAvenueID) ||
79751                     !context.hasEntity(eleventhAvenueEndID)) {
79752                     return continueTo(rightClickIntersection);
79753                 }
79754
79755                 var ids = context.selectedIDs();
79756                 var hasWashington = ids.indexOf(_washingtonSegmentID) !== -1;
79757                 var hasTwelfth = ids.indexOf(twelfthAvenueID) !== -1;
79758
79759                 if (hasWashington && hasTwelfth) {
79760                     return continueTo(multiRightClick);
79761                 } else if (!hasWashington && !hasTwelfth) {
79762                     return continueTo(didSplit);
79763                 }
79764
79765                 context.map().centerZoomEase(twelfthAvenue, 18, 500);
79766
79767                 timeout(function() {
79768                     var selected, other, padding, box;
79769                     if (hasWashington) {
79770                         selected = _t('intro.graph.name.washington-street');
79771                         other = _t('intro.graph.name.12th-avenue');
79772                         padding = 60 * Math.pow(2, context.map().zoom() - 18);
79773                         box = pad(twelfthAvenueEnd, padding, context);
79774                         box.width *= 3;
79775                     } else {
79776                         selected = _t('intro.graph.name.12th-avenue');
79777                         other = _t('intro.graph.name.washington-street');
79778                         padding = 200 * Math.pow(2, context.map().zoom() - 18);
79779                         box = pad(twelfthAvenue, padding, context);
79780                         box.width /= 2;
79781                     }
79782
79783                     reveal(box,
79784                         helpString('intro.lines.multi_select',
79785                             { selected: selected, other1: other }) + ' ' +
79786                         helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79787                             { selected: selected, other2: other })
79788                     );
79789
79790                     context.map().on('move.intro drawn.intro', function() {
79791                         if (hasWashington) {
79792                             selected = _t('intro.graph.name.washington-street');
79793                             other = _t('intro.graph.name.12th-avenue');
79794                             padding = 60 * Math.pow(2, context.map().zoom() - 18);
79795                             box = pad(twelfthAvenueEnd, padding, context);
79796                             box.width *= 3;
79797                         } else {
79798                             selected = _t('intro.graph.name.12th-avenue');
79799                             other = _t('intro.graph.name.washington-street');
79800                             padding = 200 * Math.pow(2, context.map().zoom() - 18);
79801                             box = pad(twelfthAvenue, padding, context);
79802                             box.width /= 2;
79803                         }
79804
79805                         reveal(box,
79806                             helpString('intro.lines.multi_select',
79807                                 { selected: selected, other1: other }) + ' ' +
79808                             helpString('intro.lines.add_to_selection_' + (context.lastPointerType() === 'mouse' ? 'click' : 'touch'),
79809                                 { selected: selected, other2: other }),
79810                             { duration: 0 }
79811                         );
79812                     });
79813
79814                     context.on('enter.intro', function() {
79815                         continueTo(multiSelect);
79816                     });
79817
79818                     context.history().on('change.intro', function() {
79819                         if (!_washingtonSegmentID ||
79820                             !context.hasEntity(_washingtonSegmentID) ||
79821                             !context.hasEntity(washingtonStreetID) ||
79822                             !context.hasEntity(twelfthAvenueID) ||
79823                             !context.hasEntity(eleventhAvenueEndID)) {
79824                             return continueTo(rightClickIntersection);
79825                         }
79826                     });
79827                 }, 600);
79828
79829                 function continueTo(nextStep) {
79830                     context.map().on('move.intro drawn.intro', null);
79831                     context.on('enter.intro', null);
79832                     context.history().on('change.intro', null);
79833                     nextStep();
79834                 }
79835             }
79836
79837
79838             function multiRightClick() {
79839                 if (!_washingtonSegmentID ||
79840                     !context.hasEntity(_washingtonSegmentID) ||
79841                     !context.hasEntity(washingtonStreetID) ||
79842                     !context.hasEntity(twelfthAvenueID) ||
79843                     !context.hasEntity(eleventhAvenueEndID)) {
79844                     return continueTo(rightClickIntersection);
79845                 }
79846
79847                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79848                 var box = pad(twelfthAvenue, padding, context);
79849
79850                 var rightClickString = helpString('intro.lines.multi_select_success') +
79851                     helpString('intro.lines.multi_' + (context.lastPointerType() === 'mouse' ? 'rightclick' : 'edit_menu_touch'));
79852                 reveal(box, rightClickString);
79853
79854                 context.map().on('move.intro drawn.intro', function() {
79855                     var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79856                     var box = pad(twelfthAvenue, padding, context);
79857                     reveal(box, rightClickString, { duration: 0 });
79858                 });
79859
79860                 context.ui().editMenu().on('toggled.intro', function(open) {
79861                     if (!open) { return; }
79862
79863                     timeout(function() {
79864                         var ids = context.selectedIDs();
79865                         if (ids.length === 2 &&
79866                             ids.indexOf(twelfthAvenueID) !== -1 &&
79867                             ids.indexOf(_washingtonSegmentID) !== -1) {
79868                                 var node = selectMenuItem(context, 'delete').node();
79869                                 if (!node) { return; }
79870                                 continueTo(multiDelete);
79871                         } else if (ids.length === 1 &&
79872                             ids.indexOf(_washingtonSegmentID) !== -1) {
79873                             return continueTo(multiSelect);
79874                         } else {
79875                             return continueTo(didSplit);
79876                         }
79877                     }, 300);  // after edit menu visible
79878                 });
79879
79880                 context.history().on('change.intro', function() {
79881                     if (!_washingtonSegmentID ||
79882                         !context.hasEntity(_washingtonSegmentID) ||
79883                         !context.hasEntity(washingtonStreetID) ||
79884                         !context.hasEntity(twelfthAvenueID) ||
79885                         !context.hasEntity(eleventhAvenueEndID)) {
79886                         return continueTo(rightClickIntersection);
79887                     }
79888                 });
79889
79890                 function continueTo(nextStep) {
79891                     context.map().on('move.intro drawn.intro', null);
79892                     context.ui().editMenu().on('toggled.intro', null);
79893                     context.history().on('change.intro', null);
79894                     nextStep();
79895                 }
79896             }
79897
79898
79899             function multiDelete() {
79900                 if (!_washingtonSegmentID ||
79901                     !context.hasEntity(_washingtonSegmentID) ||
79902                     !context.hasEntity(washingtonStreetID) ||
79903                     !context.hasEntity(twelfthAvenueID) ||
79904                     !context.hasEntity(eleventhAvenueEndID)) {
79905                     return continueTo(rightClickIntersection);
79906                 }
79907
79908                 var node = selectMenuItem(context, 'delete').node();
79909                 if (!node) { return continueTo(multiRightClick); }
79910
79911                 reveal('.edit-menu',
79912                     helpString('intro.lines.multi_delete'),
79913                     { padding: 50 }
79914                 );
79915
79916                 context.map().on('move.intro drawn.intro', function() {
79917                     reveal('.edit-menu',
79918                         helpString('intro.lines.multi_delete'),
79919                         { duration: 0, padding: 50 }
79920                     );
79921                 });
79922
79923                 context.on('exit.intro', function() {
79924                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79925                         return continueTo(multiSelect);  // left select mode but roads still exist
79926                     }
79927                 });
79928
79929                 context.history().on('change.intro', function() {
79930                     if (context.hasEntity(_washingtonSegmentID) || context.hasEntity(twelfthAvenueID)) {
79931                         continueTo(retryDelete);         // changed something but roads still exist
79932                     } else {
79933                         continueTo(play);
79934                     }
79935                 });
79936
79937                 function continueTo(nextStep) {
79938                     context.map().on('move.intro drawn.intro', null);
79939                     context.on('exit.intro', null);
79940                     context.history().on('change.intro', null);
79941                     nextStep();
79942                 }
79943             }
79944
79945
79946             function retryDelete() {
79947                 context.enter(modeBrowse(context));
79948
79949                 var padding = 200 * Math.pow(2, context.map().zoom() - 18);
79950                 var box = pad(twelfthAvenue, padding, context);
79951                 reveal(box, helpString('intro.lines.retry_delete'), {
79952                     buttonText: _t('intro.ok'),
79953                     buttonCallback: function() { continueTo(multiSelect); }
79954                 });
79955
79956                 function continueTo(nextStep) {
79957                     nextStep();
79958                 }
79959             }
79960
79961
79962             function play() {
79963                 dispatch$1.call('done');
79964                 reveal('.ideditor',
79965                     helpString('intro.lines.play', { next: _t('intro.buildings.title') }), {
79966                         tooltipBox: '.intro-nav-wrap .chapter-building',
79967                         buttonText: _t('intro.ok'),
79968                         buttonCallback: function() { reveal('.ideditor'); }
79969                     }
79970                 );
79971            }
79972
79973
79974             chapter.enter = function() {
79975                 addLine();
79976             };
79977
79978
79979             chapter.exit = function() {
79980                 timeouts.forEach(window.clearTimeout);
79981                 select(window).on('pointerdown.intro mousedown.intro', null, true);
79982                 context.on('enter.intro exit.intro', null);
79983                 context.map().on('move.intro drawn.intro', null);
79984                 context.history().on('change.intro', null);
79985                 context.container().select('.inspector-wrap').on('wheel.intro', null);
79986                 context.container().select('.preset-list-button').on('click.intro', null);
79987             };
79988
79989
79990             chapter.restart = function() {
79991                 chapter.exit();
79992                 chapter.enter();
79993             };
79994
79995
79996             return utilRebind(chapter, dispatch$1, 'on');
79997         }
79998
79999         function uiIntroBuilding(context, reveal) {
80000             var dispatch$1 = dispatch('done');
80001             var house = [-85.62815, 41.95638];
80002             var tank = [-85.62732, 41.95347];
80003             var buildingCatetory = _mainPresetIndex.item('category-building');
80004             var housePreset = _mainPresetIndex.item('building/house');
80005             var tankPreset = _mainPresetIndex.item('man_made/storage_tank');
80006             var timeouts = [];
80007             var _houseID = null;
80008             var _tankID = null;
80009
80010
80011             var chapter = {
80012                 title: 'intro.buildings.title'
80013             };
80014
80015
80016             function timeout(f, t) {
80017                 timeouts.push(window.setTimeout(f, t));
80018             }
80019
80020
80021             function eventCancel() {
80022                 event.stopPropagation();
80023                 event.preventDefault();
80024             }
80025
80026
80027             function revealHouse(center, text, options) {
80028                 var padding = 160 * Math.pow(2, context.map().zoom() - 20);
80029                 var box = pad(center, padding, context);
80030                 reveal(box, text, options);
80031             }
80032
80033
80034             function revealTank(center, text, options) {
80035                 var padding = 190 * Math.pow(2, context.map().zoom() - 19.5);
80036                 var box = pad(center, padding, context);
80037                 reveal(box, text, options);
80038             }
80039
80040
80041             function addHouse() {
80042                 context.enter(modeBrowse(context));
80043                 context.history().reset('initial');
80044                 _houseID = null;
80045
80046                 var msec = transitionTime(house, context.map().center());
80047                 if (msec) { reveal(null, null, { duration: 0 }); }
80048                 context.map().centerZoomEase(house, 19, msec);
80049
80050                 timeout(function() {
80051                     var tooltip = reveal('button.add-area',
80052                         helpString('intro.buildings.add_building'));
80053
80054                     tooltip.selectAll('.popover-inner')
80055                         .insert('svg', 'span')
80056                         .attr('class', 'tooltip-illustration')
80057                         .append('use')
80058                         .attr('xlink:href', '#iD-graphic-buildings');
80059
80060                     context.on('enter.intro', function(mode) {
80061                         if (mode.id !== 'add-area') { return; }
80062                         continueTo(startHouse);
80063                     });
80064                 }, msec + 100);
80065
80066                 function continueTo(nextStep) {
80067                     context.on('enter.intro', null);
80068                     nextStep();
80069                 }
80070             }
80071
80072
80073             function startHouse() {
80074                 if (context.mode().id !== 'add-area') {
80075                     return continueTo(addHouse);
80076                 }
80077
80078                 _houseID = null;
80079                 context.map().zoomEase(20, 500);
80080
80081                 timeout(function() {
80082                     var startString = helpString('intro.buildings.start_building') +
80083                         helpString('intro.buildings.building_corner_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80084                     revealHouse(house, startString);
80085
80086                     context.map().on('move.intro drawn.intro', function() {
80087                         revealHouse(house, startString, { duration: 0 });
80088                     });
80089
80090                     context.on('enter.intro', function(mode) {
80091                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80092                         continueTo(continueHouse);
80093                     });
80094
80095                 }, 550);  // after easing
80096
80097                 function continueTo(nextStep) {
80098                     context.map().on('move.intro drawn.intro', null);
80099                     context.on('enter.intro', null);
80100                     nextStep();
80101                 }
80102             }
80103
80104
80105             function continueHouse() {
80106                 if (context.mode().id !== 'draw-area') {
80107                     return continueTo(addHouse);
80108                 }
80109
80110                 _houseID = null;
80111
80112                 var continueString = helpString('intro.buildings.continue_building') + '{br}' +
80113                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80114                     helpString('intro.buildings.finish_building');
80115
80116                 revealHouse(house, continueString);
80117
80118                 context.map().on('move.intro drawn.intro', function() {
80119                     revealHouse(house, continueString, { duration: 0 });
80120                 });
80121
80122                 context.on('enter.intro', function(mode) {
80123                     if (mode.id === 'draw-area') {
80124                         return;
80125                     } else if (mode.id === 'select') {
80126                         var graph = context.graph();
80127                         var way = context.entity(context.selectedIDs()[0]);
80128                         var nodes = graph.childNodes(way);
80129                         var points = utilArrayUniq(nodes)
80130                             .map(function(n) { return context.projection(n.loc); });
80131
80132                         if (isMostlySquare(points)) {
80133                             _houseID = way.id;
80134                             return continueTo(chooseCategoryBuilding);
80135                         } else {
80136                             return continueTo(retryHouse);
80137                         }
80138
80139                     } else {
80140                         return chapter.restart();
80141                     }
80142                 });
80143
80144                 function continueTo(nextStep) {
80145                     context.map().on('move.intro drawn.intro', null);
80146                     context.on('enter.intro', null);
80147                     nextStep();
80148                 }
80149             }
80150
80151
80152             function retryHouse() {
80153                 var onClick = function() { continueTo(addHouse); };
80154
80155                 revealHouse(house, helpString('intro.buildings.retry_building'),
80156                     { buttonText: _t('intro.ok'), buttonCallback: onClick }
80157                 );
80158
80159                 context.map().on('move.intro drawn.intro', function() {
80160                     revealHouse(house, helpString('intro.buildings.retry_building'),
80161                         { duration: 0, buttonText: _t('intro.ok'), buttonCallback: onClick }
80162                     );
80163                 });
80164
80165                 function continueTo(nextStep) {
80166                     context.map().on('move.intro drawn.intro', null);
80167                     nextStep();
80168                 }
80169             }
80170
80171
80172             function chooseCategoryBuilding() {
80173                 if (!_houseID || !context.hasEntity(_houseID)) {
80174                     return addHouse();
80175                 }
80176                 var ids = context.selectedIDs();
80177                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80178                     context.enter(modeSelect(context, [_houseID]));
80179                 }
80180
80181                 // disallow scrolling
80182                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80183
80184                 timeout(function() {
80185                     // reset pane, in case user somehow happened to change it..
80186                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80187
80188                     var button = context.container().select('.preset-category-building .preset-list-button');
80189
80190                     reveal(button.node(),
80191                         helpString('intro.buildings.choose_category_building', { category: buildingCatetory.name() })
80192                     );
80193
80194                     button.on('click.intro', function() {
80195                         button.on('click.intro', null);
80196                         continueTo(choosePresetHouse);
80197                     });
80198
80199                 }, 400);  // after preset list pane visible..
80200
80201
80202                 context.on('enter.intro', function(mode) {
80203                     if (!_houseID || !context.hasEntity(_houseID)) {
80204                         return continueTo(addHouse);
80205                     }
80206                     var ids = context.selectedIDs();
80207                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80208                         return continueTo(chooseCategoryBuilding);
80209                     }
80210                 });
80211
80212                 function continueTo(nextStep) {
80213                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80214                     context.container().select('.preset-list-button').on('click.intro', null);
80215                     context.on('enter.intro', null);
80216                     nextStep();
80217                 }
80218             }
80219
80220
80221             function choosePresetHouse() {
80222                 if (!_houseID || !context.hasEntity(_houseID)) {
80223                     return addHouse();
80224                 }
80225                 var ids = context.selectedIDs();
80226                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80227                     context.enter(modeSelect(context, [_houseID]));
80228                 }
80229
80230                 // disallow scrolling
80231                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80232
80233                 timeout(function() {
80234                     // reset pane, in case user somehow happened to change it..
80235                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80236
80237                     var button = context.container().select('.preset-building-house .preset-list-button');
80238
80239                     reveal(button.node(),
80240                         helpString('intro.buildings.choose_preset_house', { preset: housePreset.name() }),
80241                         { duration: 300 }
80242                     );
80243
80244                     button.on('click.intro', function() {
80245                         button.on('click.intro', null);
80246                         continueTo(closeEditorHouse);
80247                     });
80248
80249                 }, 400);  // after preset list pane visible..
80250
80251                 context.on('enter.intro', function(mode) {
80252                     if (!_houseID || !context.hasEntity(_houseID)) {
80253                         return continueTo(addHouse);
80254                     }
80255                     var ids = context.selectedIDs();
80256                     if (mode.id !== 'select' || !ids.length || ids[0] !== _houseID) {
80257                         return continueTo(chooseCategoryBuilding);
80258                     }
80259                 });
80260
80261                 function continueTo(nextStep) {
80262                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80263                     context.container().select('.preset-list-button').on('click.intro', null);
80264                     context.on('enter.intro', null);
80265                     nextStep();
80266                 }
80267             }
80268
80269
80270             function closeEditorHouse() {
80271                 if (!_houseID || !context.hasEntity(_houseID)) {
80272                     return addHouse();
80273                 }
80274                 var ids = context.selectedIDs();
80275                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _houseID) {
80276                     context.enter(modeSelect(context, [_houseID]));
80277                 }
80278
80279                 context.history().checkpoint('hasHouse');
80280
80281                 context.on('exit.intro', function() {
80282                     continueTo(rightClickHouse);
80283                 });
80284
80285                 timeout(function() {
80286                     reveal('.entity-editor-pane',
80287                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80288                     );
80289                 }, 500);
80290
80291                 function continueTo(nextStep) {
80292                     context.on('exit.intro', null);
80293                     nextStep();
80294                 }
80295             }
80296
80297
80298             function rightClickHouse() {
80299                 if (!_houseID) { return chapter.restart(); }
80300
80301                 context.enter(modeBrowse(context));
80302                 context.history().reset('hasHouse');
80303                 var zoom = context.map().zoom();
80304                 if (zoom < 20) {
80305                     zoom = 20;
80306                 }
80307                 context.map().centerZoomEase(house, zoom, 500);
80308
80309                 context.on('enter.intro', function(mode) {
80310                     if (mode.id !== 'select') { return; }
80311                     var ids = context.selectedIDs();
80312                     if (ids.length !== 1 || ids[0] !== _houseID) { return; }
80313
80314                     timeout(function() {
80315                         var node = selectMenuItem(context, 'orthogonalize').node();
80316                         if (!node) { return; }
80317                         continueTo(clickSquare);
80318                     }, 50);  // after menu visible
80319                 });
80320
80321                 context.map().on('move.intro drawn.intro', function() {
80322                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_building' : 'edit_menu_building_touch'));
80323                     revealHouse(house, rightclickString, { duration: 0 });
80324                 });
80325
80326                 context.history().on('change.intro', function() {
80327                     continueTo(rightClickHouse);
80328                 });
80329
80330                 function continueTo(nextStep) {
80331                     context.on('enter.intro', null);
80332                     context.map().on('move.intro drawn.intro', null);
80333                     context.history().on('change.intro', null);
80334                     nextStep();
80335                 }
80336             }
80337
80338
80339             function clickSquare() {
80340                 if (!_houseID) { return chapter.restart(); }
80341                 var entity = context.hasEntity(_houseID);
80342                 if (!entity) { return continueTo(rightClickHouse); }
80343
80344                 var node = selectMenuItem(context, 'orthogonalize').node();
80345                 if (!node) { return continueTo(rightClickHouse); }
80346
80347                 var wasChanged = false;
80348
80349                 reveal('.edit-menu',
80350                     helpString('intro.buildings.square_building'),
80351                     { padding: 50 }
80352                 );
80353
80354                 context.on('enter.intro', function(mode) {
80355                     if (mode.id === 'browse') {
80356                         continueTo(rightClickHouse);
80357                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80358                         continueTo(retryClickSquare);
80359                     }
80360                 });
80361
80362                 context.map().on('move.intro', function() {
80363                     var node = selectMenuItem(context, 'orthogonalize').node();
80364                     if (!wasChanged && !node) { return continueTo(rightClickHouse); }
80365
80366                     reveal('.edit-menu',
80367                         helpString('intro.buildings.square_building'),
80368                         { duration: 0, padding: 50 }
80369                     );
80370                 });
80371
80372                 context.history().on('change.intro', function() {
80373                     wasChanged = true;
80374                     context.history().on('change.intro', null);
80375
80376                     // Something changed.  Wait for transition to complete and check undo annotation.
80377                     timeout(function() {
80378                         if (context.history().undoAnnotation() === _t('operations.orthogonalize.annotation.feature.single')) {
80379                             continueTo(doneSquare);
80380                         } else {
80381                             continueTo(retryClickSquare);
80382                         }
80383                     }, 500);  // after transitioned actions
80384                 });
80385
80386                 function continueTo(nextStep) {
80387                     context.on('enter.intro', null);
80388                     context.map().on('move.intro', null);
80389                     context.history().on('change.intro', null);
80390                     nextStep();
80391                 }
80392             }
80393
80394
80395             function retryClickSquare() {
80396                 context.enter(modeBrowse(context));
80397
80398                 revealHouse(house, helpString('intro.buildings.retry_square'), {
80399                     buttonText: _t('intro.ok'),
80400                     buttonCallback: function() { continueTo(rightClickHouse); }
80401                 });
80402
80403                 function continueTo(nextStep) {
80404                     nextStep();
80405                 }
80406             }
80407
80408
80409             function doneSquare() {
80410                 context.history().checkpoint('doneSquare');
80411
80412                 revealHouse(house, helpString('intro.buildings.done_square'), {
80413                     buttonText: _t('intro.ok'),
80414                     buttonCallback: function() { continueTo(addTank); }
80415                 });
80416
80417                 function continueTo(nextStep) {
80418                     nextStep();
80419                 }
80420             }
80421
80422
80423             function addTank() {
80424                 context.enter(modeBrowse(context));
80425                 context.history().reset('doneSquare');
80426                 _tankID = null;
80427
80428                 var msec = transitionTime(tank, context.map().center());
80429                 if (msec) { reveal(null, null, { duration: 0 }); }
80430                 context.map().centerZoomEase(tank, 19.5, msec);
80431
80432                 timeout(function() {
80433                     reveal('button.add-area',
80434                         helpString('intro.buildings.add_tank')
80435                     );
80436
80437                     context.on('enter.intro', function(mode) {
80438                         if (mode.id !== 'add-area') { return; }
80439                         continueTo(startTank);
80440                     });
80441                 }, msec + 100);
80442
80443                 function continueTo(nextStep) {
80444                     context.on('enter.intro', null);
80445                     nextStep();
80446                 }
80447             }
80448
80449
80450             function startTank() {
80451                 if (context.mode().id !== 'add-area') {
80452                     return continueTo(addTank);
80453                 }
80454
80455                 _tankID = null;
80456
80457                 timeout(function() {
80458                     var startString = helpString('intro.buildings.start_tank') +
80459                         helpString('intro.buildings.tank_edge_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap'));
80460                     revealTank(tank, startString);
80461
80462                     context.map().on('move.intro drawn.intro', function() {
80463                         revealTank(tank, startString, { duration: 0 });
80464                     });
80465
80466                     context.on('enter.intro', function(mode) {
80467                         if (mode.id !== 'draw-area') { return chapter.restart(); }
80468                         continueTo(continueTank);
80469                     });
80470
80471                 }, 550);  // after easing
80472
80473                 function continueTo(nextStep) {
80474                     context.map().on('move.intro drawn.intro', null);
80475                     context.on('enter.intro', null);
80476                     nextStep();
80477                 }
80478             }
80479
80480
80481             function continueTank() {
80482                 if (context.mode().id !== 'draw-area') {
80483                     return continueTo(addTank);
80484                 }
80485
80486                 _tankID = null;
80487
80488                 var continueString = helpString('intro.buildings.continue_tank') + '{br}' +
80489                     helpString('intro.areas.finish_area_' + (context.lastPointerType() === 'mouse' ? 'click' : 'tap')) +
80490                     helpString('intro.buildings.finish_tank');
80491
80492                 revealTank(tank, continueString);
80493
80494                 context.map().on('move.intro drawn.intro', function() {
80495                     revealTank(tank, continueString, { duration: 0 });
80496                 });
80497
80498                 context.on('enter.intro', function(mode) {
80499                     if (mode.id === 'draw-area') {
80500                         return;
80501                     } else if (mode.id === 'select') {
80502                         _tankID = context.selectedIDs()[0];
80503                         return continueTo(searchPresetTank);
80504                     } else {
80505                         return continueTo(addTank);
80506                     }
80507                 });
80508
80509                 function continueTo(nextStep) {
80510                     context.map().on('move.intro drawn.intro', null);
80511                     context.on('enter.intro', null);
80512                     nextStep();
80513                 }
80514             }
80515
80516
80517             function searchPresetTank() {
80518                 if (!_tankID || !context.hasEntity(_tankID)) {
80519                     return addTank();
80520                 }
80521                 var ids = context.selectedIDs();
80522                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80523                     context.enter(modeSelect(context, [_tankID]));
80524                 }
80525
80526                 // disallow scrolling
80527                 context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80528
80529                 timeout(function() {
80530                     // reset pane, in case user somehow happened to change it..
80531                     context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80532
80533                     context.container().select('.preset-search-input')
80534                         .on('keydown.intro', null)
80535                         .on('keyup.intro', checkPresetSearch);
80536
80537                     reveal('.preset-search-input',
80538                         helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80539                     );
80540                 }, 400);  // after preset list pane visible..
80541
80542                 context.on('enter.intro', function(mode) {
80543                     if (!_tankID || !context.hasEntity(_tankID)) {
80544                         return continueTo(addTank);
80545                     }
80546
80547                     var ids = context.selectedIDs();
80548                     if (mode.id !== 'select' || !ids.length || ids[0] !== _tankID) {
80549                         // keep the user's area selected..
80550                         context.enter(modeSelect(context, [_tankID]));
80551
80552                         // reset pane, in case user somehow happened to change it..
80553                         context.container().select('.inspector-wrap .panewrap').style('right', '-100%');
80554                         // disallow scrolling
80555                         context.container().select('.inspector-wrap').on('wheel.intro', eventCancel);
80556
80557                         context.container().select('.preset-search-input')
80558                             .on('keydown.intro', null)
80559                             .on('keyup.intro', checkPresetSearch);
80560
80561                         reveal('.preset-search-input',
80562                             helpString('intro.buildings.search_tank', { preset: tankPreset.name() })
80563                         );
80564
80565                         context.history().on('change.intro', null);
80566                     }
80567                 });
80568
80569                 function checkPresetSearch() {
80570                     var first = context.container().select('.preset-list-item:first-child');
80571
80572                     if (first.classed('preset-man_made-storage_tank')) {
80573                         reveal(first.select('.preset-list-button').node(),
80574                             helpString('intro.buildings.choose_tank', { preset: tankPreset.name() }),
80575                             { duration: 300 }
80576                         );
80577
80578                         context.container().select('.preset-search-input')
80579                             .on('keydown.intro', eventCancel, true)
80580                             .on('keyup.intro', null);
80581
80582                         context.history().on('change.intro', function() {
80583                             continueTo(closeEditorTank);
80584                         });
80585                     }
80586                 }
80587
80588                 function continueTo(nextStep) {
80589                     context.container().select('.inspector-wrap').on('wheel.intro', null);
80590                     context.on('enter.intro', null);
80591                     context.history().on('change.intro', null);
80592                     context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80593                     nextStep();
80594                 }
80595             }
80596
80597
80598             function closeEditorTank() {
80599                 if (!_tankID || !context.hasEntity(_tankID)) {
80600                     return addTank();
80601                 }
80602                 var ids = context.selectedIDs();
80603                 if (context.mode().id !== 'select' || !ids.length || ids[0] !== _tankID) {
80604                     context.enter(modeSelect(context, [_tankID]));
80605                 }
80606
80607                 context.history().checkpoint('hasTank');
80608
80609                 context.on('exit.intro', function() {
80610                     continueTo(rightClickTank);
80611                 });
80612
80613                 timeout(function() {
80614                     reveal('.entity-editor-pane',
80615                         helpString('intro.buildings.close', { button: icon('#iD-icon-close', 'pre-text') })
80616                     );
80617                 }, 500);
80618
80619                 function continueTo(nextStep) {
80620                     context.on('exit.intro', null);
80621                     nextStep();
80622                 }
80623             }
80624
80625
80626             function rightClickTank() {
80627                 if (!_tankID) { return continueTo(addTank); }
80628
80629                 context.enter(modeBrowse(context));
80630                 context.history().reset('hasTank');
80631                 context.map().centerEase(tank, 500);
80632
80633                 timeout(function() {
80634                     context.on('enter.intro', function(mode) {
80635                         if (mode.id !== 'select') { return; }
80636                         var ids = context.selectedIDs();
80637                         if (ids.length !== 1 || ids[0] !== _tankID) { return; }
80638
80639                         timeout(function() {
80640                             var node = selectMenuItem(context, 'circularize').node();
80641                             if (!node) { return; }
80642                             continueTo(clickCircle);
80643                         }, 50);  // after menu visible
80644                     });
80645
80646                     var rightclickString = helpString('intro.buildings.' + (context.lastPointerType() === 'mouse' ? 'rightclick_tank' : 'edit_menu_tank_touch'));
80647
80648                     revealTank(tank, rightclickString);
80649
80650                     context.map().on('move.intro drawn.intro', function() {
80651                         revealTank(tank, rightclickString, { duration: 0 });
80652                     });
80653
80654                     context.history().on('change.intro', function() {
80655                         continueTo(rightClickTank);
80656                     });
80657
80658                 }, 600);
80659
80660                 function continueTo(nextStep) {
80661                     context.on('enter.intro', null);
80662                     context.map().on('move.intro drawn.intro', null);
80663                     context.history().on('change.intro', null);
80664                     nextStep();
80665                 }
80666             }
80667
80668
80669             function clickCircle() {
80670                 if (!_tankID) { return chapter.restart(); }
80671                 var entity = context.hasEntity(_tankID);
80672                 if (!entity) { return continueTo(rightClickTank); }
80673
80674                 var node = selectMenuItem(context, 'circularize').node();
80675                 if (!node) { return continueTo(rightClickTank); }
80676
80677                 var wasChanged = false;
80678
80679                 reveal('.edit-menu',
80680                     helpString('intro.buildings.circle_tank'),
80681                     { padding: 50 }
80682                 );
80683
80684                 context.on('enter.intro', function(mode) {
80685                     if (mode.id === 'browse') {
80686                         continueTo(rightClickTank);
80687                     } else if (mode.id === 'move' || mode.id === 'rotate') {
80688                         continueTo(retryClickCircle);
80689                     }
80690                 });
80691
80692                 context.map().on('move.intro', function() {
80693                     var node = selectMenuItem(context, 'circularize').node();
80694                     if (!wasChanged && !node) { return continueTo(rightClickTank); }
80695
80696                     reveal('.edit-menu',
80697                         helpString('intro.buildings.circle_tank'),
80698                         { duration: 0, padding: 50 }
80699                     );
80700                 });
80701
80702                 context.history().on('change.intro', function() {
80703                     wasChanged = true;
80704                     context.history().on('change.intro', null);
80705
80706                     // Something changed.  Wait for transition to complete and check undo annotation.
80707                     timeout(function() {
80708                         if (context.history().undoAnnotation() === _t('operations.circularize.annotation.single')) {
80709                             continueTo(play);
80710                         } else {
80711                             continueTo(retryClickCircle);
80712                         }
80713                     }, 500);  // after transitioned actions
80714                 });
80715
80716                 function continueTo(nextStep) {
80717                     context.on('enter.intro', null);
80718                     context.map().on('move.intro', null);
80719                     context.history().on('change.intro', null);
80720                     nextStep();
80721                 }
80722             }
80723
80724
80725             function retryClickCircle() {
80726                 context.enter(modeBrowse(context));
80727
80728                 revealTank(tank, helpString('intro.buildings.retry_circle'), {
80729                     buttonText: _t('intro.ok'),
80730                     buttonCallback: function() { continueTo(rightClickTank); }
80731                 });
80732
80733                 function continueTo(nextStep) {
80734                     nextStep();
80735                 }
80736             }
80737
80738
80739             function play() {
80740                 dispatch$1.call('done');
80741                 reveal('.ideditor',
80742                     helpString('intro.buildings.play', { next: _t('intro.startediting.title') }), {
80743                         tooltipBox: '.intro-nav-wrap .chapter-startEditing',
80744                         buttonText: _t('intro.ok'),
80745                         buttonCallback: function() { reveal('.ideditor'); }
80746                     }
80747                 );
80748             }
80749
80750
80751             chapter.enter = function() {
80752                 addHouse();
80753             };
80754
80755
80756             chapter.exit = function() {
80757                 timeouts.forEach(window.clearTimeout);
80758                 context.on('enter.intro exit.intro', null);
80759                 context.map().on('move.intro drawn.intro', null);
80760                 context.history().on('change.intro', null);
80761                 context.container().select('.inspector-wrap').on('wheel.intro', null);
80762                 context.container().select('.preset-search-input').on('keydown.intro keyup.intro', null);
80763                 context.container().select('.more-fields .combobox-input').on('click.intro', null);
80764             };
80765
80766
80767             chapter.restart = function() {
80768                 chapter.exit();
80769                 chapter.enter();
80770             };
80771
80772
80773             return utilRebind(chapter, dispatch$1, 'on');
80774         }
80775
80776         function uiIntroStartEditing(context, reveal) {
80777             var dispatch$1 = dispatch('done', 'startEditing');
80778             var modalSelection = select(null);
80779
80780
80781             var chapter = {
80782                 title: 'intro.startediting.title'
80783             };
80784
80785             function showHelp() {
80786                 reveal('.map-control.help-control',
80787                     helpString('intro.startediting.help'), {
80788                         buttonText: _t('intro.ok'),
80789                         buttonCallback: function() { shortcuts(); }
80790                     }
80791                 );
80792             }
80793
80794             function shortcuts() {
80795                 reveal('.map-control.help-control',
80796                     helpString('intro.startediting.shortcuts'), {
80797                         buttonText: _t('intro.ok'),
80798                         buttonCallback: function() { showSave(); }
80799                     }
80800                 );
80801             }
80802
80803             function showSave() {
80804                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80805                 reveal('.top-toolbar button.save',
80806                     helpString('intro.startediting.save'), {
80807                         buttonText: _t('intro.ok'),
80808                         buttonCallback: function() { showStart(); }
80809                     }
80810                 );
80811             }
80812
80813             function showStart() {
80814                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80815
80816                 modalSelection = uiModal(context.container());
80817
80818                 modalSelection.select('.modal')
80819                     .attr('class', 'modal-splash modal');
80820
80821                 modalSelection.selectAll('.close').remove();
80822
80823                 var startbutton = modalSelection.select('.content')
80824                     .attr('class', 'fillL')
80825                     .append('button')
80826                         .attr('class', 'modal-section huge-modal-button')
80827                         .on('click', function() {
80828                             modalSelection.remove();
80829                         });
80830
80831                     startbutton
80832                         .append('svg')
80833                         .attr('class', 'illustration')
80834                         .append('use')
80835                         .attr('xlink:href', '#iD-logo-walkthrough');
80836
80837                     startbutton
80838                         .append('h2')
80839                         .text(_t('intro.startediting.start'));
80840
80841                 dispatch$1.call('startEditing');
80842             }
80843
80844
80845             chapter.enter = function() {
80846                 showHelp();
80847             };
80848
80849
80850             chapter.exit = function() {
80851                 modalSelection.remove();
80852                 context.container().selectAll('.shaded').remove();  // in case user opened keyboard shortcuts
80853             };
80854
80855
80856             return utilRebind(chapter, dispatch$1, 'on');
80857         }
80858
80859         var chapterUi = {
80860           welcome: uiIntroWelcome,
80861           navigation: uiIntroNavigation,
80862           point: uiIntroPoint,
80863           area: uiIntroArea,
80864           line: uiIntroLine,
80865           building: uiIntroBuilding,
80866           startEditing: uiIntroStartEditing
80867         };
80868
80869         var chapterFlow = [
80870           'welcome',
80871           'navigation',
80872           'point',
80873           'area',
80874           'line',
80875           'building',
80876           'startEditing'
80877         ];
80878
80879
80880         function uiIntro(context) {
80881           var INTRO_IMAGERY = 'EsriWorldImageryClarity';
80882           var _introGraph = {};
80883           var _currChapter;
80884
80885
80886           function intro(selection) {
80887             _mainFileFetcher.get('intro_graph')
80888               .then(function (dataIntroGraph) {
80889                 // create entities for intro graph and localize names
80890                 for (var id in dataIntroGraph) {
80891                   if (!_introGraph[id]) {
80892                     _introGraph[id] = osmEntity(localize(dataIntroGraph[id]));
80893                   }
80894                 }
80895                 selection.call(startIntro);
80896               })
80897               .catch(function() { /* ignore */ });
80898           }
80899
80900
80901           function startIntro(selection) {
80902             context.enter(modeBrowse(context));
80903
80904             // Save current map state
80905             var osm = context.connection();
80906             var history = context.history().toJSON();
80907             var hash = window.location.hash;
80908             var center = context.map().center();
80909             var zoom = context.map().zoom();
80910             var background = context.background().baseLayerSource();
80911             var overlays = context.background().overlayLayerSources();
80912             var opacity = context.container().selectAll('.main-map .layer-background').style('opacity');
80913             var caches = osm && osm.caches();
80914             var baseEntities = context.history().graph().base().entities;
80915
80916             // Show sidebar and disable the sidebar resizing button
80917             // (this needs to be before `context.inIntro(true)`)
80918             context.ui().sidebar.expand();
80919             context.container().selectAll('button.sidebar-toggle').classed('disabled', true);
80920
80921             // Block saving
80922             context.inIntro(true);
80923
80924             // Load semi-real data used in intro
80925             if (osm) { osm.toggle(false).reset(); }
80926             context.history().reset();
80927             context.history().merge(Object.values(coreGraph().load(_introGraph).entities));
80928             context.history().checkpoint('initial');
80929
80930             // Setup imagery
80931             var imagery = context.background().findSource(INTRO_IMAGERY);
80932             if (imagery) {
80933               context.background().baseLayerSource(imagery);
80934             } else {
80935               context.background().bing();
80936             }
80937             overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
80938
80939             // Setup data layers (only OSM)
80940             var layers = context.layers();
80941             layers.all().forEach(function (item) {
80942               // if the layer has the function `enabled`
80943               if (typeof item.layer.enabled === 'function') {
80944                 item.layer.enabled(item.id === 'osm');
80945               }
80946             });
80947
80948
80949             context.container().selectAll('.main-map .layer-background').style('opacity', 1);
80950
80951             var curtain = uiCurtain(context.container().node());
80952             selection.call(curtain);
80953
80954             // Store that the user started the walkthrough..
80955             corePreferences('walkthrough_started', 'yes');
80956
80957             // Restore previous walkthrough progress..
80958             var storedProgress = corePreferences('walkthrough_progress') || '';
80959             var progress = storedProgress.split(';').filter(Boolean);
80960
80961             var chapters = chapterFlow.map(function (chapter, i) {
80962               var s = chapterUi[chapter](context, curtain.reveal)
80963                 .on('done', function () {
80964
80965                   buttons
80966                     .filter(function (d) { return d.title === s.title; })
80967                     .classed('finished', true);
80968
80969                   if (i < chapterFlow.length - 1) {
80970                     var next = chapterFlow[i + 1];
80971                     context.container().select(("button.chapter-" + next))
80972                       .classed('next', true);
80973                   }
80974
80975                   // Store walkthrough progress..
80976                   progress.push(chapter);
80977                   corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80978                 });
80979               return s;
80980             });
80981
80982             chapters[chapters.length - 1].on('startEditing', function () {
80983               // Store walkthrough progress..
80984               progress.push('startEditing');
80985               corePreferences('walkthrough_progress', utilArrayUniq(progress).join(';'));
80986
80987               // Store if walkthrough is completed..
80988               var incomplete = utilArrayDifference(chapterFlow, progress);
80989               if (!incomplete.length) {
80990                 corePreferences('walkthrough_completed', 'yes');
80991               }
80992
80993               curtain.remove();
80994               navwrap.remove();
80995               context.container().selectAll('.main-map .layer-background').style('opacity', opacity);
80996               context.container().selectAll('button.sidebar-toggle').classed('disabled', false);
80997               if (osm) { osm.toggle(true).reset().caches(caches); }
80998               context.history().reset().merge(Object.values(baseEntities));
80999               context.background().baseLayerSource(background);
81000               overlays.forEach(function (d) { return context.background().toggleOverlayLayer(d); });
81001               if (history) { context.history().fromJSON(history, false); }
81002               context.map().centerZoom(center, zoom);
81003               window.location.replace(hash);
81004               context.inIntro(false);
81005             });
81006
81007             var navwrap = selection
81008               .append('div')
81009               .attr('class', 'intro-nav-wrap fillD');
81010
81011             navwrap
81012               .append('svg')
81013               .attr('class', 'intro-nav-wrap-logo')
81014               .append('use')
81015               .attr('xlink:href', '#iD-logo-walkthrough');
81016
81017             var buttonwrap = navwrap
81018               .append('div')
81019               .attr('class', 'joined')
81020               .selectAll('button.chapter');
81021
81022             var buttons = buttonwrap
81023               .data(chapters)
81024               .enter()
81025               .append('button')
81026               .attr('class', function (d, i) { return ("chapter chapter-" + (chapterFlow[i])); })
81027               .on('click', enterChapter);
81028
81029             buttons
81030               .append('span')
81031               .text(function (d) { return _t(d.title); });
81032
81033             buttons
81034               .append('span')
81035               .attr('class', 'status')
81036               .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
81037
81038             enterChapter(chapters[0]);
81039
81040
81041             function enterChapter(newChapter) {
81042               if (_currChapter) { _currChapter.exit(); }
81043               context.enter(modeBrowse(context));
81044
81045               _currChapter = newChapter;
81046               _currChapter.enter();
81047
81048               buttons
81049                 .classed('next', false)
81050                 .classed('active', function (d) { return d.title === _currChapter.title; });
81051             }
81052           }
81053
81054
81055           return intro;
81056         }
81057
81058         function uiIssuesInfo(context) {
81059
81060             var warningsItem = {
81061                 id: 'warnings',
81062                 count: 0,
81063                 iconID: 'iD-icon-alert',
81064                 descriptionID: 'issues.warnings_and_errors'
81065             };
81066
81067             var resolvedItem = {
81068                 id: 'resolved',
81069                 count: 0,
81070                 iconID: 'iD-icon-apply',
81071                 descriptionID: 'issues.user_resolved_issues'
81072             };
81073
81074             function update(selection) {
81075
81076                 var shownItems = [];
81077
81078                 var liveIssues = context.validator().getIssues({
81079                     what: corePreferences('validate-what') || 'edited',
81080                     where: corePreferences('validate-where') || 'all'
81081                 });
81082                 if (liveIssues.length) {
81083                     warningsItem.count = liveIssues.length;
81084                     shownItems.push(warningsItem);
81085                 }
81086
81087                 if (corePreferences('validate-what') === 'all') {
81088                     var resolvedIssues = context.validator().getResolvedIssues();
81089                     if (resolvedIssues.length) {
81090                         resolvedItem.count = resolvedIssues.length;
81091                         shownItems.push(resolvedItem);
81092                     }
81093                 }
81094
81095                 var chips = selection.selectAll('.chip')
81096                     .data(shownItems, function(d) {
81097                         return d.id;
81098                     });
81099
81100                 chips.exit().remove();
81101
81102                 var enter = chips.enter()
81103                     .append('a')
81104                     .attr('class', function(d) {
81105                         return 'chip ' + d.id + '-count';
81106                     })
81107                     .attr('href', '#')
81108                     .attr('tabindex', -1)
81109                     .each(function(d) {
81110
81111                         var chipSelection = select(this);
81112
81113                         var tooltipBehavior = uiTooltip()
81114                             .placement('top')
81115                             .title(_t(d.descriptionID));
81116
81117                         chipSelection
81118                             .call(tooltipBehavior)
81119                             .on('click', function() {
81120                                 event.preventDefault();
81121
81122                                 tooltipBehavior.hide(select(this));
81123                                 // open the Issues pane
81124                                 context.ui().togglePanes(context.container().select('.map-panes .issues-pane'));
81125                             });
81126
81127                         chipSelection.call(svgIcon('#' + d.iconID));
81128
81129                     });
81130
81131                 enter.append('span')
81132                     .attr('class', 'count');
81133
81134                 enter.merge(chips)
81135                     .selectAll('span.count')
81136                     .text(function(d) {
81137                         return d.count.toString();
81138                     });
81139             }
81140
81141
81142             return function(selection) {
81143                 update(selection);
81144
81145                 context.validator().on('validated.infobox', function() {
81146                     update(selection);
81147                 });
81148             };
81149         }
81150
81151         // import { utilGetDimensions } from '../util/dimensions';
81152
81153
81154         function uiMapInMap(context) {
81155
81156             function mapInMap(selection) {
81157                 var backgroundLayer = rendererTileLayer(context);
81158                 var overlayLayers = {};
81159                 var projection = geoRawMercator();
81160                 var dataLayer = svgData(projection, context).showLabels(false);
81161                 var debugLayer = svgDebug(projection, context);
81162                 var zoom = d3_zoom()
81163                     .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(24)])
81164                     .on('start', zoomStarted)
81165                     .on('zoom', zoomed)
81166                     .on('end', zoomEnded);
81167
81168                 var wrap = select(null);
81169                 var tiles = select(null);
81170                 var viewport = select(null);
81171
81172                 var _isTransformed = false;
81173                 var _isHidden = true;
81174                 var _skipEvents = false;
81175                 var _gesture = null;
81176                 var _zDiff = 6;    // by default, minimap renders at (main zoom - 6)
81177                 var _dMini;        // dimensions of minimap
81178                 var _cMini;        // center pixel of minimap
81179                 var _tStart;       // transform at start of gesture
81180                 var _tCurr;        // transform at most recent event
81181                 var _timeoutID;
81182
81183
81184                 function zoomStarted() {
81185                     if (_skipEvents) { return; }
81186                     _tStart = _tCurr = projection.transform();
81187                     _gesture = null;
81188                 }
81189
81190
81191                 function zoomed() {
81192                     if (_skipEvents) { return; }
81193
81194                     var x = event.transform.x;
81195                     var y = event.transform.y;
81196                     var k = event.transform.k;
81197                     var isZooming = (k !== _tStart.k);
81198                     var isPanning = (x !== _tStart.x || y !== _tStart.y);
81199
81200                     if (!isZooming && !isPanning) {
81201                         return;  // no change
81202                     }
81203
81204                     // lock in either zooming or panning, don't allow both in minimap.
81205                     if (!_gesture) {
81206                         _gesture = isZooming ? 'zoom' : 'pan';
81207                     }
81208
81209                     var tMini = projection.transform();
81210                     var tX, tY, scale;
81211
81212                     if (_gesture === 'zoom') {
81213                         scale = k / tMini.k;
81214                         tX = (_cMini[0] / scale - _cMini[0]) * scale;
81215                         tY = (_cMini[1] / scale - _cMini[1]) * scale;
81216                     } else {
81217                         k = tMini.k;
81218                         scale = 1;
81219                         tX = x - tMini.x;
81220                         tY = y - tMini.y;
81221                     }
81222
81223                     utilSetTransform(tiles, tX, tY, scale);
81224                     utilSetTransform(viewport, 0, 0, scale);
81225                     _isTransformed = true;
81226                     _tCurr = identity$2.translate(x, y).scale(k);
81227
81228                     var zMain = geoScaleToZoom(context.projection.scale());
81229                     var zMini = geoScaleToZoom(k);
81230
81231                     _zDiff = zMain - zMini;
81232
81233                     queueRedraw();
81234                 }
81235
81236
81237                 function zoomEnded() {
81238                     if (_skipEvents) { return; }
81239                     if (_gesture !== 'pan') { return; }
81240
81241                     updateProjection();
81242                     _gesture = null;
81243                     context.map().center(projection.invert(_cMini));   // recenter main map..
81244                 }
81245
81246
81247                 function updateProjection() {
81248                     var loc = context.map().center();
81249                     var tMain = context.projection.transform();
81250                     var zMain = geoScaleToZoom(tMain.k);
81251                     var zMini = Math.max(zMain - _zDiff, 0.5);
81252                     var kMini = geoZoomToScale(zMini);
81253
81254                     projection
81255                         .translate([tMain.x, tMain.y])
81256                         .scale(kMini);
81257
81258                     var point = projection(loc);
81259                     var mouse = (_gesture === 'pan') ? geoVecSubtract([_tCurr.x, _tCurr.y], [_tStart.x, _tStart.y]) : [0, 0];
81260                     var xMini = _cMini[0] - point[0] + tMain.x + mouse[0];
81261                     var yMini = _cMini[1] - point[1] + tMain.y + mouse[1];
81262
81263                     projection
81264                         .translate([xMini, yMini])
81265                         .clipExtent([[0, 0], _dMini]);
81266
81267                     _tCurr = projection.transform();
81268
81269                     if (_isTransformed) {
81270                         utilSetTransform(tiles, 0, 0);
81271                         utilSetTransform(viewport, 0, 0);
81272                         _isTransformed = false;
81273                     }
81274
81275                     zoom
81276                         .scaleExtent([geoZoomToScale(0.5), geoZoomToScale(zMain - 3)]);
81277
81278                     _skipEvents = true;
81279                     wrap.call(zoom.transform, _tCurr);
81280                     _skipEvents = false;
81281                 }
81282
81283
81284                 function redraw() {
81285                     clearTimeout(_timeoutID);
81286                     if (_isHidden) { return; }
81287
81288                     updateProjection();
81289                     var zMini = geoScaleToZoom(projection.scale());
81290
81291                     // setup tile container
81292                     tiles = wrap
81293                         .selectAll('.map-in-map-tiles')
81294                         .data([0]);
81295
81296                     tiles = tiles.enter()
81297                         .append('div')
81298                         .attr('class', 'map-in-map-tiles')
81299                         .merge(tiles);
81300
81301                     // redraw background
81302                     backgroundLayer
81303                         .source(context.background().baseLayerSource())
81304                         .projection(projection)
81305                         .dimensions(_dMini);
81306
81307                     var background = tiles
81308                         .selectAll('.map-in-map-background')
81309                         .data([0]);
81310
81311                     background.enter()
81312                         .append('div')
81313                         .attr('class', 'map-in-map-background')
81314                         .merge(background)
81315                         .call(backgroundLayer);
81316
81317
81318                     // redraw overlay
81319                     var overlaySources = context.background().overlayLayerSources();
81320                     var activeOverlayLayers = [];
81321                     for (var i = 0; i < overlaySources.length; i++) {
81322                         if (overlaySources[i].validZoom(zMini)) {
81323                             if (!overlayLayers[i]) { overlayLayers[i] = rendererTileLayer(context); }
81324                             activeOverlayLayers.push(overlayLayers[i]
81325                                 .source(overlaySources[i])
81326                                 .projection(projection)
81327                                 .dimensions(_dMini));
81328                         }
81329                     }
81330
81331                     var overlay = tiles
81332                         .selectAll('.map-in-map-overlay')
81333                         .data([0]);
81334
81335                     overlay = overlay.enter()
81336                         .append('div')
81337                         .attr('class', 'map-in-map-overlay')
81338                         .merge(overlay);
81339
81340
81341                     var overlays = overlay
81342                         .selectAll('div')
81343                         .data(activeOverlayLayers, function(d) { return d.source().name(); });
81344
81345                     overlays.exit()
81346                         .remove();
81347
81348                     overlays = overlays.enter()
81349                         .append('div')
81350                         .merge(overlays)
81351                         .each(function(layer) { select(this).call(layer); });
81352
81353
81354                     var dataLayers = tiles
81355                         .selectAll('.map-in-map-data')
81356                         .data([0]);
81357
81358                     dataLayers.exit()
81359                         .remove();
81360
81361                     dataLayers = dataLayers.enter()
81362                         .append('svg')
81363                         .attr('class', 'map-in-map-data')
81364                         .merge(dataLayers)
81365                         .call(dataLayer)
81366                         .call(debugLayer);
81367
81368
81369                     // redraw viewport bounding box
81370                     if (_gesture !== 'pan') {
81371                         var getPath = d3_geoPath(projection);
81372                         var bbox = { type: 'Polygon', coordinates: [context.map().extent().polygon()] };
81373
81374                         viewport = wrap.selectAll('.map-in-map-viewport')
81375                             .data([0]);
81376
81377                         viewport = viewport.enter()
81378                             .append('svg')
81379                             .attr('class', 'map-in-map-viewport')
81380                             .merge(viewport);
81381
81382
81383                         var path = viewport.selectAll('.map-in-map-bbox')
81384                             .data([bbox]);
81385
81386                         path.enter()
81387                             .append('path')
81388                             .attr('class', 'map-in-map-bbox')
81389                             .merge(path)
81390                             .attr('d', getPath)
81391                             .classed('thick', function(d) { return getPath.area(d) < 30; });
81392                     }
81393                 }
81394
81395
81396                 function queueRedraw() {
81397                     clearTimeout(_timeoutID);
81398                     _timeoutID = setTimeout(function() { redraw(); }, 750);
81399                 }
81400
81401
81402                 function toggle() {
81403                     if (event) { event.preventDefault(); }
81404
81405                     _isHidden = !_isHidden;
81406
81407                     context.container().select('.minimap-toggle-item')
81408                         .classed('active', !_isHidden)
81409                         .select('input')
81410                         .property('checked', !_isHidden);
81411
81412                     if (_isHidden) {
81413                         wrap
81414                             .style('display', 'block')
81415                             .style('opacity', '1')
81416                             .transition()
81417                             .duration(200)
81418                             .style('opacity', '0')
81419                             .on('end', function() {
81420                                 selection.selectAll('.map-in-map')
81421                                     .style('display', 'none');
81422                             });
81423                     } else {
81424                         wrap
81425                             .style('display', 'block')
81426                             .style('opacity', '0')
81427                             .transition()
81428                             .duration(200)
81429                             .style('opacity', '1')
81430                             .on('end', function() {
81431                                 redraw();
81432                             });
81433                     }
81434                 }
81435
81436
81437                 uiMapInMap.toggle = toggle;
81438
81439                 wrap = selection.selectAll('.map-in-map')
81440                     .data([0]);
81441
81442                 wrap = wrap.enter()
81443                     .append('div')
81444                     .attr('class', 'map-in-map')
81445                     .style('display', (_isHidden ? 'none' : 'block'))
81446                     .call(zoom)
81447                     .on('dblclick.zoom', null)
81448                     .merge(wrap);
81449
81450                 // reflow warning: Hardcode dimensions - currently can't resize it anyway..
81451                 _dMini = [200,150]; //utilGetDimensions(wrap);
81452                 _cMini = geoVecScale(_dMini, 0.5);
81453
81454                 context.map()
81455                     .on('drawn.map-in-map', function(drawn) {
81456                         if (drawn.full === true) {
81457                             redraw();
81458                         }
81459                     });
81460
81461                 redraw();
81462
81463                 context.keybinding()
81464                     .on(_t('background.minimap.key'), toggle);
81465             }
81466
81467             return mapInMap;
81468         }
81469
81470         function uiNotice(context) {
81471
81472             return function(selection) {
81473                 var div = selection
81474                     .append('div')
81475                     .attr('class', 'notice');
81476
81477                 var button = div
81478                     .append('button')
81479                     .attr('class', 'zoom-to notice fillD')
81480                     .on('click', function() {
81481                         context.map().zoomEase(context.minEditableZoom());
81482                     })
81483                     .on('wheel', function() {   // let wheel events pass through #4482
81484                         var e2 = new WheelEvent(event.type, event);
81485                         context.surface().node().dispatchEvent(e2);
81486                     });
81487
81488                 button
81489                     .call(svgIcon('#iD-icon-plus', 'pre-text'))
81490                     .append('span')
81491                     .attr('class', 'label')
81492                     .text(_t('zoom_in_edit'));
81493
81494
81495                 function disableTooHigh() {
81496                     var canEdit = context.map().zoom() >= context.minEditableZoom();
81497                     div.style('display', canEdit ? 'none' : 'block');
81498                 }
81499
81500                 context.map()
81501                     .on('move.notice', debounce(disableTooHigh, 500));
81502
81503                 disableTooHigh();
81504             };
81505         }
81506
81507         function uiPhotoviewer(context) {
81508
81509             var dispatch$1 = dispatch('resize');
81510
81511             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
81512
81513             function photoviewer(selection) {
81514                 selection
81515                     .append('button')
81516                     .attr('class', 'thumb-hide')
81517                     .on('click', function () {
81518                         if (services.streetside) { services.streetside.hideViewer(context); }
81519                         if (services.mapillary) { services.mapillary.hideViewer(context); }
81520                         if (services.openstreetcam) { services.openstreetcam.hideViewer(context); }
81521                     })
81522                     .append('div')
81523                     .call(svgIcon('#iD-icon-close'));
81524
81525                 function preventDefault() {
81526                     event.preventDefault();
81527                 }
81528
81529                 selection
81530                     .append('button')
81531                     .attr('class', 'resize-handle-xy')
81532                     .on('touchstart touchdown touchend', preventDefault)
81533                     .on(
81534                         _pointerPrefix + 'down',
81535                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true, resizeOnY: true })
81536                     );
81537
81538                 selection
81539                     .append('button')
81540                     .attr('class', 'resize-handle-x')
81541                     .on('touchstart touchdown touchend', preventDefault)
81542                     .on(
81543                         _pointerPrefix + 'down',
81544                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnX: true })
81545                     );
81546
81547                 selection
81548                     .append('button')
81549                     .attr('class', 'resize-handle-y')
81550                     .on('touchstart touchdown touchend', preventDefault)
81551                     .on(
81552                         _pointerPrefix + 'down',
81553                         buildResizeListener(selection, 'resize', dispatch$1, { resizeOnY: true })
81554                     );
81555
81556                 services.streetside.loadViewer(context);
81557                 services.mapillary.loadViewer(context);
81558                 services.openstreetcam.loadViewer(context);
81559
81560                 function buildResizeListener(target, eventName, dispatch, options) {
81561
81562                     var resizeOnX = !!options.resizeOnX;
81563                     var resizeOnY = !!options.resizeOnY;
81564                     var minHeight = options.minHeight || 240;
81565                     var minWidth = options.minWidth || 320;
81566                     var pointerId;
81567                     var startX;
81568                     var startY;
81569                     var startWidth;
81570                     var startHeight;
81571
81572                     function startResize() {
81573                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81574
81575                         event.preventDefault();
81576                         event.stopPropagation();
81577
81578                         var mapSize = context.map().dimensions();
81579
81580                         if (resizeOnX) {
81581                             var maxWidth = mapSize[0];
81582                             var newWidth = clamp((startWidth + event.clientX - startX), minWidth, maxWidth);
81583                             target.style('width', newWidth + 'px');
81584                         }
81585
81586                         if (resizeOnY) {
81587                             var maxHeight = mapSize[1] - 90;  // preserve space at top/bottom of map
81588                             var newHeight = clamp((startHeight + startY - event.clientY), minHeight, maxHeight);
81589                             target.style('height', newHeight + 'px');
81590                         }
81591
81592                         dispatch.call(eventName, target, utilGetDimensions(target, true));
81593                     }
81594
81595                     function clamp(num, min, max) {
81596                         return Math.max(min, Math.min(num, max));
81597                     }
81598
81599                     function stopResize() {
81600                         if (pointerId !== (event.pointerId || 'mouse')) { return; }
81601
81602                         event.preventDefault();
81603                         event.stopPropagation();
81604
81605                         // remove all the listeners we added
81606                         select(window)
81607                             .on('.' + eventName, null);
81608                     }
81609
81610                     return function initResize() {
81611                         event.preventDefault();
81612                         event.stopPropagation();
81613
81614                         pointerId = event.pointerId || 'mouse';
81615
81616                         startX = event.clientX;
81617                         startY = event.clientY;
81618                         var targetRect = target.node().getBoundingClientRect();
81619                         startWidth = targetRect.width;
81620                         startHeight = targetRect.height;
81621
81622                         select(window)
81623                             .on(_pointerPrefix + 'move.' + eventName, startResize, false)
81624                             .on(_pointerPrefix + 'up.' + eventName, stopResize, false);
81625
81626                         if (_pointerPrefix === 'pointer') {
81627                             select(window)
81628                                 .on('pointercancel.' + eventName, stopResize, false);
81629                         }
81630                     };
81631                 }
81632             }
81633
81634             photoviewer.onMapResize = function() {
81635                 var photoviewer = context.container().select('.photoviewer');
81636                 var content = context.container().select('.main-content');
81637                 var mapDimensions = utilGetDimensions(content, true);
81638                 // shrink photo viewer if it is too big
81639                 // (-90 preserves space at top and bottom of map used by menus)
81640                 var photoDimensions = utilGetDimensions(photoviewer, true);
81641                 if (photoDimensions[0] > mapDimensions[0] || photoDimensions[1] > (mapDimensions[1] - 90)) {
81642                     var setPhotoDimensions = [
81643                         Math.min(photoDimensions[0], mapDimensions[0]),
81644                         Math.min(photoDimensions[1], mapDimensions[1] - 90) ];
81645
81646                     photoviewer
81647                         .style('width', setPhotoDimensions[0] + 'px')
81648                         .style('height', setPhotoDimensions[1] + 'px');
81649
81650                     dispatch$1.call('resize', photoviewer, setPhotoDimensions);
81651                 }
81652             };
81653
81654             return utilRebind(photoviewer, dispatch$1, 'on');
81655         }
81656
81657         function uiRestore(context) {
81658           return function(selection) {
81659             if (!context.history().hasRestorableChanges()) { return; }
81660
81661             var modalSelection = uiModal(selection, true);
81662
81663             modalSelection.select('.modal')
81664               .attr('class', 'modal fillL');
81665
81666             var introModal = modalSelection.select('.content');
81667
81668             introModal
81669               .append('div')
81670               .attr('class', 'modal-section')
81671               .append('h3')
81672               .text(_t('restore.heading'));
81673
81674             introModal
81675               .append('div')
81676               .attr('class','modal-section')
81677               .append('p')
81678               .text(_t('restore.description'));
81679
81680             var buttonWrap = introModal
81681               .append('div')
81682               .attr('class', 'modal-actions');
81683
81684             var restore = buttonWrap
81685               .append('button')
81686               .attr('class', 'restore')
81687               .on('click', function () {
81688                 context.history().restore();
81689                 modalSelection.remove();
81690               });
81691
81692             restore
81693               .append('svg')
81694               .attr('class', 'logo logo-restore')
81695               .append('use')
81696               .attr('xlink:href', '#iD-logo-restore');
81697
81698             restore
81699               .append('div')
81700               .text(_t('restore.restore'));
81701
81702             var reset = buttonWrap
81703               .append('button')
81704               .attr('class', 'reset')
81705               .on('click', function () {
81706                 context.history().clearSaved();
81707                 modalSelection.remove();
81708               });
81709
81710             reset
81711               .append('svg')
81712               .attr('class', 'logo logo-reset')
81713               .append('use')
81714               .attr('xlink:href', '#iD-logo-reset');
81715
81716             reset
81717               .append('div')
81718               .text(_t('restore.reset'));
81719
81720             restore.node().focus();
81721           };
81722         }
81723
81724         function uiScale(context) {
81725             var projection = context.projection,
81726                 isImperial = !_mainLocalizer.usesMetric(),
81727                 maxLength = 180,
81728                 tickHeight = 8;
81729
81730
81731             function scaleDefs(loc1, loc2) {
81732                 var lat = (loc2[1] + loc1[1]) / 2,
81733                     conversion = (isImperial ? 3.28084 : 1),
81734                     dist = geoLonToMeters(loc2[0] - loc1[0], lat) * conversion,
81735                     scale = { dist: 0, px: 0, text: '' },
81736                     buckets, i, val, dLon;
81737
81738                 if (isImperial) {
81739                     buckets = [5280000, 528000, 52800, 5280, 500, 50, 5, 1];
81740                 } else {
81741                     buckets = [5000000, 500000, 50000, 5000, 500, 50, 5, 1];
81742                 }
81743
81744                 // determine a user-friendly endpoint for the scale
81745                 for (i = 0; i < buckets.length; i++) {
81746                     val = buckets[i];
81747                     if (dist >= val) {
81748                         scale.dist = Math.floor(dist / val) * val;
81749                         break;
81750                     } else {
81751                         scale.dist = +dist.toFixed(2);
81752                     }
81753                 }
81754
81755                 dLon = geoMetersToLon(scale.dist / conversion, lat);
81756                 scale.px = Math.round(projection([loc1[0] + dLon, loc1[1]])[0]);
81757
81758                 scale.text = displayLength(scale.dist / conversion, isImperial);
81759
81760                 return scale;
81761             }
81762
81763
81764             function update(selection) {
81765                 // choose loc1, loc2 along bottom of viewport (near where the scale will be drawn)
81766                 var dims = context.map().dimensions(),
81767                     loc1 = projection.invert([0, dims[1]]),
81768                     loc2 = projection.invert([maxLength, dims[1]]),
81769                     scale = scaleDefs(loc1, loc2);
81770
81771                 selection.select('.scale-path')
81772                     .attr('d', 'M0.5,0.5v' + tickHeight + 'h' + scale.px + 'v-' + tickHeight);
81773
81774                 selection.select('.scale-textgroup')
81775                     .attr('transform', 'translate(' + (scale.px + 8) + ',' + tickHeight + ')');
81776
81777                 selection.select('.scale-text')
81778                     .text(scale.text);
81779             }
81780
81781
81782             return function(selection) {
81783                 function switchUnits() {
81784                     isImperial = !isImperial;
81785                     selection.call(update);
81786                 }
81787
81788                 var scalegroup = selection.append('svg')
81789                     .attr('class', 'scale')
81790                     .on('click', switchUnits)
81791                     .append('g')
81792                     .attr('transform', 'translate(10,11)');
81793
81794                 scalegroup
81795                     .append('path')
81796                     .attr('class', 'scale-path');
81797
81798                 scalegroup
81799                     .append('g')
81800                     .attr('class', 'scale-textgroup')
81801                     .append('text')
81802                     .attr('class', 'scale-text');
81803
81804                 selection.call(update);
81805
81806                 context.map().on('move.scale', function() {
81807                     update(selection);
81808                 });
81809             };
81810         }
81811
81812         function uiShortcuts(context) {
81813             var detected = utilDetect();
81814             var _activeTab = 0;
81815             var _modalSelection;
81816             var _selection = select(null);
81817
81818
81819             context.keybinding()
81820                 .on([_t('shortcuts.toggle.key'), '?'], function () {
81821                     if (context.container().selectAll('.modal-shortcuts').size()) {  // already showing
81822                         if (_modalSelection) {
81823                             _modalSelection.close();
81824                             _modalSelection = null;
81825                         }
81826                     } else {
81827                         _modalSelection = uiModal(_selection);
81828                         _modalSelection.call(shortcutsModal);
81829                     }
81830                 });
81831
81832
81833             function shortcutsModal(_modalSelection) {
81834                 _modalSelection.select('.modal')
81835                     .classed('modal-shortcuts', true);
81836
81837                 var content = _modalSelection.select('.content');
81838
81839                 content
81840                     .append('div')
81841                     .attr('class', 'modal-section')
81842                     .append('h3')
81843                     .text(_t('shortcuts.title'));
81844
81845                 _mainFileFetcher.get('shortcuts')
81846                     .then(function(data) { content.call(render, data); })
81847                     .catch(function() { /* ignore */ });
81848             }
81849
81850
81851             function render(selection, dataShortcuts) {
81852                 var wrapper = selection
81853                     .selectAll('.wrapper')
81854                     .data([0]);
81855
81856                 var wrapperEnter = wrapper
81857                     .enter()
81858                     .append('div')
81859                     .attr('class', 'wrapper modal-section');
81860
81861                 var tabsBar = wrapperEnter
81862                     .append('div')
81863                     .attr('class', 'tabs-bar');
81864
81865                 var shortcutsList = wrapperEnter
81866                     .append('div')
81867                     .attr('class', 'shortcuts-list');
81868
81869                 wrapper = wrapper.merge(wrapperEnter);
81870
81871                 var tabs = tabsBar
81872                     .selectAll('.tab')
81873                     .data(dataShortcuts);
81874
81875                 var tabsEnter = tabs
81876                     .enter()
81877                     .append('div')
81878                     .attr('class', 'tab')
81879                     .on('click', function (d, i) {
81880                         _activeTab = i;
81881                         render(selection, dataShortcuts);
81882                     });
81883
81884                 tabsEnter
81885                     .append('span')
81886                     .text(function (d) { return _t(d.text); });
81887
81888                 tabs = tabs
81889                     .merge(tabsEnter);
81890
81891                 // Update
81892                 wrapper.selectAll('.tab')
81893                     .classed('active', function (d, i) {
81894                         return i === _activeTab;
81895                     });
81896
81897
81898                 var shortcuts = shortcutsList
81899                     .selectAll('.shortcut-tab')
81900                     .data(dataShortcuts);
81901
81902                 var shortcutsEnter = shortcuts
81903                     .enter()
81904                     .append('div')
81905                     .attr('class', function(d) { return 'shortcut-tab shortcut-tab-' + d.tab; });
81906
81907                 var columnsEnter = shortcutsEnter
81908                     .selectAll('.shortcut-column')
81909                     .data(function (d) { return d.columns; })
81910                     .enter()
81911                     .append('table')
81912                     .attr('class', 'shortcut-column');
81913
81914                 var rowsEnter = columnsEnter
81915                     .selectAll('.shortcut-row')
81916                     .data(function (d) { return d.rows; })
81917                     .enter()
81918                     .append('tr')
81919                     .attr('class', 'shortcut-row');
81920
81921
81922                 var sectionRows = rowsEnter
81923                     .filter(function (d) { return !d.shortcuts; });
81924
81925                 sectionRows
81926                     .append('td');
81927
81928                 sectionRows
81929                     .append('td')
81930                     .attr('class', 'shortcut-section')
81931                     .append('h3')
81932                     .text(function (d) { return _t(d.text); });
81933
81934
81935                 var shortcutRows = rowsEnter
81936                     .filter(function (d) { return d.shortcuts; });
81937
81938                 var shortcutKeys = shortcutRows
81939                     .append('td')
81940                     .attr('class', 'shortcut-keys');
81941
81942                 var modifierKeys = shortcutKeys
81943                     .filter(function (d) { return d.modifiers; });
81944
81945                 modifierKeys
81946                     .selectAll('kbd.modifier')
81947                     .data(function (d) {
81948                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81949                             return ['⌘'];
81950                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81951                             return [];
81952                         } else {
81953                             return d.modifiers;
81954                         }
81955                     })
81956                     .enter()
81957                     .each(function () {
81958                         var selection = select(this);
81959
81960                         selection
81961                             .append('kbd')
81962                             .attr('class', 'modifier')
81963                             .text(function (d) { return uiCmd.display(d); });
81964
81965                         selection
81966                             .append('span')
81967                             .text('+');
81968                     });
81969
81970
81971                 shortcutKeys
81972                     .selectAll('kbd.shortcut')
81973                     .data(function (d) {
81974                         var arr = d.shortcuts;
81975                         if (detected.os === 'win' && d.text === 'shortcuts.editing.commands.redo') {
81976                             arr = ['Y'];
81977                         } else if (detected.os !== 'mac' && d.text === 'shortcuts.browsing.display_options.fullscreen') {
81978                             arr = ['F11'];
81979                         }
81980
81981                         // replace translations
81982                         arr = arr.map(function(s) {
81983                             return uiCmd.display(s.indexOf('.') !== -1 ? _t(s) : s);
81984                         });
81985
81986                         return utilArrayUniq(arr).map(function(s) {
81987                             return {
81988                                 shortcut: s,
81989                                 separator: d.separator,
81990                                 suffix: d.suffix
81991                             };
81992                         });
81993                     })
81994                     .enter()
81995                     .each(function (d, i, nodes) {
81996                         var selection = select(this);
81997                         var click = d.shortcut.toLowerCase().match(/(.*).click/);
81998
81999                         if (click && click[1]) {   // replace "left_click", "right_click" with mouse icon
82000                             selection
82001                                 .call(svgIcon('#iD-walkthrough-mouse-' + click[1], 'operation'));
82002                         } else if (d.shortcut.toLowerCase() === 'long-press') {
82003                             selection
82004                                 .call(svgIcon('#iD-walkthrough-longpress', 'longpress operation'));
82005                         } else if (d.shortcut.toLowerCase() === 'tap') {
82006                             selection
82007                                 .call(svgIcon('#iD-walkthrough-tap', 'tap operation'));
82008                         } else {
82009                             selection
82010                                 .append('kbd')
82011                                 .attr('class', 'shortcut')
82012                                 .text(function (d) { return d.shortcut; });
82013                         }
82014
82015                         if (i < nodes.length - 1) {
82016                             selection
82017                                 .append('span')
82018                                 .text(d.separator || '\u00a0' + _t('shortcuts.or') + '\u00a0');
82019                         } else if (i === nodes.length - 1 && d.suffix) {
82020                             selection
82021                                 .append('span')
82022                                 .text(d.suffix);
82023                         }
82024                     });
82025
82026
82027                 shortcutKeys
82028                     .filter(function(d) { return d.gesture; })
82029                     .each(function () {
82030                         var selection = select(this);
82031
82032                         selection
82033                             .append('span')
82034                             .text('+');
82035
82036                         selection
82037                             .append('span')
82038                             .attr('class', 'gesture')
82039                             .text(function (d) { return _t(d.gesture); });
82040                     });
82041
82042
82043                 shortcutRows
82044                     .append('td')
82045                     .attr('class', 'shortcut-desc')
82046                     .text(function (d) { return d.text ? _t(d.text) : '\u00a0'; });
82047
82048
82049                 shortcuts = shortcuts
82050                     .merge(shortcutsEnter);
82051
82052                 // Update
82053                 wrapper.selectAll('.shortcut-tab')
82054                     .style('display', function (d, i) {
82055                         return i === _activeTab ? 'flex' : 'none';
82056                     });
82057             }
82058
82059
82060             return function(selection, show) {
82061                 _selection = selection;
82062                 if (show) {
82063                     _modalSelection = uiModal(selection);
82064                     _modalSelection.call(shortcutsModal);
82065                 }
82066             };
82067         }
82068
82069         var pair_1 = pair;
82070
82071
82072         function search(input, dims) {
82073           if (!dims) { dims = 'NSEW'; }
82074           if (typeof input !== 'string') { return null; }
82075
82076           input = input.toUpperCase();
82077           var regex = /^[\s\,]*([NSEW])?\s*([\-|\—|\―]?[0-9.]+)[°º˚]?\s*(?:([0-9.]+)['’′‘]\s*)?(?:([0-9.]+)(?:''|"|”|″)\s*)?([NSEW])?/;
82078
82079           var m = input.match(regex);
82080           if (!m) { return null; }  // no match
82081
82082           var matched = m[0];
82083
82084           // extract dimension.. m[1] = leading, m[5] = trailing
82085           var dim;
82086           if (m[1] && m[5]) {                 // if matched both..
82087             dim = m[1];                       // keep leading
82088             matched = matched.slice(0, -1);   // remove trailing dimension from match
82089           } else {
82090             dim = m[1] || m[5];
82091           }
82092
82093           // if unrecognized dimension
82094           if (dim && dims.indexOf(dim) === -1) { return null; }
82095
82096           // extract DMS
82097           var deg = m[2] ? parseFloat(m[2]) : 0;
82098           var min = m[3] ? parseFloat(m[3]) / 60 : 0;
82099           var sec = m[4] ? parseFloat(m[4]) / 3600 : 0;
82100           var sign = (deg < 0) ? -1 : 1;
82101           if (dim === 'S' || dim === 'W') { sign *= -1; }
82102
82103           return {
82104             val: (Math.abs(deg) + min + sec) * sign,
82105             dim: dim,
82106             matched: matched,
82107             remain: input.slice(matched.length)
82108           };
82109         }
82110
82111
82112         function pair(input, dims) {
82113           input = input.trim();
82114           var one = search(input, dims);
82115           if (!one) { return null; }
82116
82117           input = one.remain.trim();
82118           var two = search(input, dims);
82119           if (!two || two.remain) { return null; }
82120
82121           if (one.dim) {
82122             return swapdim(one.val, two.val, one.dim);
82123           } else {
82124             return [one.val, two.val];
82125           }
82126         }
82127
82128
82129         function swapdim(a, b, dim) {
82130           if (dim === 'N' || dim === 'S') { return [a, b]; }
82131           if (dim === 'W' || dim === 'E') { return [b, a]; }
82132         }
82133
82134         function uiFeatureList(context) {
82135             var _geocodeResults;
82136
82137
82138             function featureList(selection) {
82139                 var header = selection
82140                     .append('div')
82141                     .attr('class', 'header fillL cf');
82142
82143                 header
82144                     .append('h3')
82145                     .text(_t('inspector.feature_list'));
82146
82147                 var searchWrap = selection
82148                     .append('div')
82149                     .attr('class', 'search-header');
82150
82151                 var search = searchWrap
82152                     .append('input')
82153                     .attr('placeholder', _t('inspector.search'))
82154                     .attr('type', 'search')
82155                     .call(utilNoAuto)
82156                     .on('keypress', keypress)
82157                     .on('keydown', keydown)
82158                     .on('input', inputevent);
82159
82160                 searchWrap
82161                     .call(svgIcon('#iD-icon-search', 'pre-text'));
82162
82163                 var listWrap = selection
82164                     .append('div')
82165                     .attr('class', 'inspector-body');
82166
82167                 var list = listWrap
82168                     .append('div')
82169                     .attr('class', 'feature-list cf');
82170
82171                 context
82172                     .on('exit.feature-list', clearSearch);
82173                 context.map()
82174                     .on('drawn.feature-list', mapDrawn);
82175
82176                 context.keybinding()
82177                     .on(uiCmd('⌘F'), focusSearch);
82178
82179
82180                 function focusSearch() {
82181                     var mode = context.mode() && context.mode().id;
82182                     if (mode !== 'browse') { return; }
82183
82184                     event.preventDefault();
82185                     search.node().focus();
82186                 }
82187
82188
82189                 function keydown() {
82190                     if (event.keyCode === 27) {  // escape
82191                         search.node().blur();
82192                     }
82193                 }
82194
82195
82196                 function keypress() {
82197                     var q = search.property('value'),
82198                         items = list.selectAll('.feature-list-item');
82199                     if (event.keyCode === 13 && q.length && items.size()) {  // return
82200                         click(items.datum());
82201                     }
82202                 }
82203
82204
82205                 function inputevent() {
82206                     _geocodeResults = undefined;
82207                     drawList();
82208                 }
82209
82210
82211                 function clearSearch() {
82212                     search.property('value', '');
82213                     drawList();
82214                 }
82215
82216
82217                 function mapDrawn(e) {
82218                     if (e.full) {
82219                         drawList();
82220                     }
82221                 }
82222
82223
82224                 function features() {
82225                     var result = [];
82226                     var graph = context.graph();
82227                     var visibleCenter = context.map().extent().center();
82228                     var q = search.property('value').toLowerCase();
82229
82230                     if (!q) { return result; }
82231
82232                     var idMatch = q.match(/(?:^|\W)(node|way|relation|[nwr])\W?0*([1-9]\d*)(?:\W|$)/i);
82233
82234                     if (idMatch) {
82235                         var elemType = idMatch[1].charAt(0);
82236                         var elemId = idMatch[2];
82237                         result.push({
82238                             id: elemType + elemId,
82239                             geometry: elemType === 'n' ? 'point' : elemType === 'w' ? 'line' : 'relation',
82240                             type: elemType === 'n' ? _t('inspector.node') : elemType === 'w' ? _t('inspector.way') : _t('inspector.relation'),
82241                             name: elemId
82242                         });
82243                     }
82244
82245                     var locationMatch = pair_1(q.toUpperCase()) || q.match(/^(-?\d+\.?\d*)\s+(-?\d+\.?\d*)$/);
82246
82247                     if (locationMatch) {
82248                         var loc = [parseFloat(locationMatch[0]), parseFloat(locationMatch[1])];
82249                         result.push({
82250                             id: -1,
82251                             geometry: 'point',
82252                             type: _t('inspector.location'),
82253                             name: dmsCoordinatePair([loc[1], loc[0]]),
82254                             location: loc
82255                         });
82256                     }
82257
82258                     var allEntities = graph.entities;
82259                     var localResults = [];
82260                     for (var id in allEntities) {
82261                         var entity = allEntities[id];
82262                         if (!entity) { continue; }
82263
82264                         var name = utilDisplayName(entity) || '';
82265                         if (name.toLowerCase().indexOf(q) < 0) { continue; }
82266
82267                         var matched = _mainPresetIndex.match(entity, graph);
82268                         var type = (matched && matched.name()) || utilDisplayType(entity.id);
82269                         var extent = entity.extent(graph);
82270                         var distance = extent ? geoSphericalDistance(visibleCenter, extent.center()) : 0;
82271
82272                         localResults.push({
82273                             id: entity.id,
82274                             entity: entity,
82275                             geometry: entity.geometry(graph),
82276                             type: type,
82277                             name: name,
82278                             distance: distance
82279                         });
82280
82281                         if (localResults.length > 100) { break; }
82282                     }
82283                     localResults = localResults.sort(function byDistance(a, b) {
82284                         return a.distance - b.distance;
82285                     });
82286                     result = result.concat(localResults);
82287
82288                     (_geocodeResults || []).forEach(function(d) {
82289                         if (d.osm_type && d.osm_id) {    // some results may be missing these - #1890
82290
82291                             // Make a temporary osmEntity so we can preset match
82292                             // and better localize the search result - #4725
82293                             var id = osmEntity.id.fromOSM(d.osm_type, d.osm_id);
82294                             var tags = {};
82295                             tags[d.class] = d.type;
82296
82297                             var attrs = { id: id, type: d.osm_type, tags: tags };
82298                             if (d.osm_type === 'way') {   // for ways, add some fake closed nodes
82299                                 attrs.nodes = ['a','a'];  // so that geometry area is possible
82300                             }
82301
82302                             var tempEntity = osmEntity(attrs);
82303                             var tempGraph = coreGraph([tempEntity]);
82304                             var matched = _mainPresetIndex.match(tempEntity, tempGraph);
82305                             var type = (matched && matched.name()) || utilDisplayType(id);
82306
82307                             result.push({
82308                                 id: tempEntity.id,
82309                                 geometry: tempEntity.geometry(tempGraph),
82310                                 type: type,
82311                                 name: d.display_name,
82312                                 extent: new geoExtent(
82313                                     [parseFloat(d.boundingbox[3]), parseFloat(d.boundingbox[0])],
82314                                     [parseFloat(d.boundingbox[2]), parseFloat(d.boundingbox[1])])
82315                             });
82316                         }
82317                     });
82318
82319                     if (q.match(/^[0-9]+$/)) {
82320                         // if query is just a number, possibly an OSM ID without a prefix
82321                         result.push({
82322                             id: 'n' + q,
82323                             geometry: 'point',
82324                             type: _t('inspector.node'),
82325                             name: q
82326                         });
82327                         result.push({
82328                             id: 'w' + q,
82329                             geometry: 'line',
82330                             type: _t('inspector.way'),
82331                             name: q
82332                         });
82333                         result.push({
82334                             id: 'r' + q,
82335                             geometry: 'relation',
82336                             type: _t('inspector.relation'),
82337                             name: q
82338                         });
82339                     }
82340
82341                     return result;
82342                 }
82343
82344
82345                 function drawList() {
82346                     var value = search.property('value');
82347                     var results = features();
82348
82349                     list.classed('filtered', value.length);
82350
82351                     var resultsIndicator = list.selectAll('.no-results-item')
82352                         .data([0])
82353                         .enter()
82354                         .append('button')
82355                         .property('disabled', true)
82356                         .attr('class', 'no-results-item')
82357                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
82358
82359                     resultsIndicator.append('span')
82360                         .attr('class', 'entity-name');
82361
82362                     list.selectAll('.no-results-item .entity-name')
82363                         .text(_t('geocoder.no_results_worldwide'));
82364
82365                     if (services.geocoder) {
82366                       list.selectAll('.geocode-item')
82367                           .data([0])
82368                           .enter()
82369                           .append('button')
82370                           .attr('class', 'geocode-item')
82371                           .on('click', geocoderSearch)
82372                           .append('div')
82373                           .attr('class', 'label')
82374                           .append('span')
82375                           .attr('class', 'entity-name')
82376                           .text(_t('geocoder.search'));
82377                     }
82378
82379                     list.selectAll('.no-results-item')
82380                         .style('display', (value.length && !results.length) ? 'block' : 'none');
82381
82382                     list.selectAll('.geocode-item')
82383                         .style('display', (value && _geocodeResults === undefined) ? 'block' : 'none');
82384
82385                     list.selectAll('.feature-list-item')
82386                         .data([-1])
82387                         .remove();
82388
82389                     var items = list.selectAll('.feature-list-item')
82390                         .data(results, function(d) { return d.id; });
82391
82392                     var enter = items.enter()
82393                         .insert('button', '.geocode-item')
82394                         .attr('class', 'feature-list-item')
82395                         .on('mouseover', mouseover)
82396                         .on('mouseout', mouseout)
82397                         .on('click', click);
82398
82399                     var label = enter
82400                         .append('div')
82401                         .attr('class', 'label');
82402
82403                     label
82404                         .each(function(d) {
82405                             select(this)
82406                                 .call(svgIcon('#iD-icon-' + d.geometry, 'pre-text'));
82407                         });
82408
82409                     label
82410                         .append('span')
82411                         .attr('class', 'entity-type')
82412                         .text(function(d) { return d.type; });
82413
82414                     label
82415                         .append('span')
82416                         .attr('class', 'entity-name')
82417                         .text(function(d) { return d.name; });
82418
82419                     enter
82420                         .style('opacity', 0)
82421                         .transition()
82422                         .style('opacity', 1);
82423
82424                     items.order();
82425
82426                     items.exit()
82427                         .remove();
82428                 }
82429
82430
82431                 function mouseover(d) {
82432                     if (d.id === -1) { return; }
82433
82434                     utilHighlightEntities([d.id], true, context);
82435                 }
82436
82437
82438                 function mouseout(d) {
82439                     if (d.id === -1) { return; }
82440
82441                     utilHighlightEntities([d.id], false, context);
82442                 }
82443
82444
82445                 function click(d) {
82446                     event.preventDefault();
82447
82448                     if (d.location) {
82449                         context.map().centerZoomEase([d.location[1], d.location[0]], 19);
82450
82451                     } else if (d.entity) {
82452                         utilHighlightEntities([d.id], false, context);
82453
82454                         context.enter(modeSelect(context, [d.entity.id]));
82455                         context.map().zoomToEase(d.entity);
82456
82457                     } else {
82458                         // download, zoom to, and select the entity with the given ID
82459                         context.zoomToEntity(d.id);
82460                     }
82461                 }
82462
82463
82464                 function geocoderSearch() {
82465                     services.geocoder.search(search.property('value'), function (err, resp) {
82466                         _geocodeResults = resp || [];
82467                         drawList();
82468                     });
82469                 }
82470             }
82471
82472
82473             return featureList;
82474         }
82475
82476         function uiSectionEntityIssues(context) {
82477
82478             var _entityIDs = [];
82479             var _issues = [];
82480             var _activeIssueID;
82481
82482             var section = uiSection('entity-issues', context)
82483                 .shouldDisplay(function() {
82484                     return _issues.length > 0;
82485                 })
82486                 .title(function() {
82487                     return _t('issues.list_title', { count: _issues.length });
82488                 })
82489                 .disclosureContent(renderDisclosureContent);
82490
82491             context.validator()
82492                 .on('validated.entity_issues', function() {
82493                     // Refresh on validated events
82494                     reloadIssues();
82495                     section.reRender();
82496                 })
82497                 .on('focusedIssue.entity_issues', function(issue) {
82498                      makeActiveIssue(issue.id);
82499                 });
82500
82501             function reloadIssues() {
82502                 _issues = context.validator().getSharedEntityIssues(_entityIDs, { includeDisabledRules: true });
82503             }
82504
82505             function makeActiveIssue(issueID) {
82506                 _activeIssueID = issueID;
82507                 section.selection().selectAll('.issue-container')
82508                     .classed('active', function(d) { return d.id === _activeIssueID; });
82509             }
82510
82511             function renderDisclosureContent(selection) {
82512
82513                 selection.classed('grouped-items-area', true);
82514
82515                 _activeIssueID = _issues.length > 0 ? _issues[0].id : null;
82516
82517                 var containers = selection.selectAll('.issue-container')
82518                     .data(_issues, function(d) { return d.id; });
82519
82520                 // Exit
82521                 containers.exit()
82522                     .remove();
82523
82524                 // Enter
82525                 var containersEnter = containers.enter()
82526                     .append('div')
82527                     .attr('class', 'issue-container');
82528
82529
82530                 var itemsEnter = containersEnter
82531                     .append('div')
82532                     .attr('class', function(d) { return 'issue severity-' + d.severity; })
82533                     .on('mouseover.highlight', function(d) {
82534                         // don't hover-highlight the selected entity
82535                         var ids = d.entityIds
82536                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82537
82538                         utilHighlightEntities(ids, true, context);
82539                     })
82540                     .on('mouseout.highlight', function(d) {
82541                         var ids = d.entityIds
82542                             .filter(function(e) { return _entityIDs.indexOf(e) === -1; });
82543
82544                         utilHighlightEntities(ids, false, context);
82545                     });
82546
82547                 var labelsEnter = itemsEnter
82548                     .append('div')
82549                     .attr('class', 'issue-label')
82550                     .on('click', function(d) {
82551
82552                         makeActiveIssue(d.id); // expand only the clicked item
82553
82554                         var extent = d.extent(context.graph());
82555                         if (extent) {
82556                             var setZoom = Math.max(context.map().zoom(), 19);
82557                             context.map().unobscuredCenterZoomEase(extent.center(), setZoom);
82558                         }
82559                     });
82560
82561                 var textEnter = labelsEnter
82562                     .append('span')
82563                     .attr('class', 'issue-text');
82564
82565                 textEnter
82566                     .append('span')
82567                     .attr('class', 'issue-icon')
82568                     .each(function(d) {
82569                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
82570                         select(this)
82571                             .call(svgIcon(iconName));
82572                     });
82573
82574                 textEnter
82575                     .append('span')
82576                     .attr('class', 'issue-message');
82577
82578
82579                 var infoButton = labelsEnter
82580                     .append('button')
82581                     .attr('class', 'issue-info-button')
82582                     .attr('title', _t('icons.information'))
82583                     .attr('tabindex', -1)
82584                     .call(svgIcon('#iD-icon-inspect'));
82585
82586                 infoButton
82587                     .on('click', function () {
82588                         event.stopPropagation();
82589                         event.preventDefault();
82590                         this.blur();    // avoid keeping focus on the button - #4641
82591
82592                         var container = select(this.parentNode.parentNode.parentNode);
82593                         var info = container.selectAll('.issue-info');
82594                         var isExpanded = info.classed('expanded');
82595
82596                         if (isExpanded) {
82597                             info
82598                                 .transition()
82599                                 .duration(200)
82600                                 .style('max-height', '0px')
82601                                 .style('opacity', '0')
82602                                 .on('end', function () {
82603                                     info.classed('expanded', false);
82604                                 });
82605                         } else {
82606                             info
82607                                 .classed('expanded', true)
82608                                 .transition()
82609                                 .duration(200)
82610                                 .style('max-height', '200px')
82611                                 .style('opacity', '1')
82612                                 .on('end', function () {
82613                                     info.style('max-height', null);
82614                                 });
82615                         }
82616                     });
82617
82618                 itemsEnter
82619                     .append('ul')
82620                     .attr('class', 'issue-fix-list');
82621
82622                 containersEnter
82623                     .append('div')
82624                     .attr('class', 'issue-info')
82625                     .style('max-height', '0')
82626                     .style('opacity', '0')
82627                     .each(function(d) {
82628                         if (typeof d.reference === 'function') {
82629                             select(this)
82630                                 .call(d.reference);
82631                         } else {
82632                             select(this)
82633                                 .text(_t('inspector.no_documentation_key'));
82634                         }
82635                     });
82636
82637
82638                 // Update
82639                 containers = containers
82640                     .merge(containersEnter)
82641                     .classed('active', function(d) { return d.id === _activeIssueID; });
82642
82643                 containers.selectAll('.issue-message')
82644                     .text(function(d) {
82645                         return d.message(context);
82646                     });
82647
82648                 // fixes
82649                 var fixLists = containers.selectAll('.issue-fix-list');
82650
82651                 var fixes = fixLists.selectAll('.issue-fix-item')
82652                     .data(function(d) { return d.fixes ? d.fixes(context) : []; }, function(fix) { return fix.id; });
82653
82654                 fixes.exit()
82655                     .remove();
82656
82657                 var fixesEnter = fixes.enter()
82658                     .append('li')
82659                     .attr('class', 'issue-fix-item')
82660                     .on('click', function(d) {
82661                         // not all fixes are actionable
82662                         if (!select(this).classed('actionable') || !d.onClick) { return; }
82663
82664                         // Don't run another fix for this issue within a second of running one
82665                         // (Necessary for "Select a feature type" fix. Most fixes should only ever run once)
82666                         if (d.issue.dateLastRanFix && new Date() - d.issue.dateLastRanFix < 1000) { return; }
82667                         d.issue.dateLastRanFix = new Date();
82668
82669                         // remove hover-highlighting
82670                         utilHighlightEntities(d.issue.entityIds.concat(d.entityIds), false, context);
82671
82672                         new Promise(function(resolve, reject) {
82673                             d.onClick(context, resolve, reject);
82674                             if (d.onClick.length <= 1) {
82675                                 // if the fix doesn't take any completion parameters then consider it resolved
82676                                 resolve();
82677                             }
82678                         })
82679                         .then(function() {
82680                             // revalidate whenever the fix has finished running successfully
82681                             context.validator().validate();
82682                         });
82683                     })
82684                     .on('mouseover.highlight', function(d) {
82685                         utilHighlightEntities(d.entityIds, true, context);
82686                     })
82687                     .on('mouseout.highlight', function(d) {
82688                         utilHighlightEntities(d.entityIds, false, context);
82689                     });
82690
82691                 fixesEnter
82692                     .append('span')
82693                     .attr('class', 'fix-icon')
82694                     .each(function(d) {
82695                         var iconName = d.icon || 'iD-icon-wrench';
82696                         if (iconName.startsWith('maki')) {
82697                             iconName += '-15';
82698                         }
82699                         select(this).call(svgIcon('#' + iconName));
82700                     });
82701
82702                 fixesEnter
82703                     .append('span')
82704                     .attr('class', 'fix-message')
82705                     .text(function(d) { return d.title; });
82706
82707                 fixesEnter.merge(fixes)
82708                     .classed('actionable', function(d) {
82709                         return d.onClick;
82710                     })
82711                     .attr('title', function(d) {
82712                         if (d.disabledReason) {
82713                             return d.disabledReason;
82714                         }
82715                         return null;
82716                     });
82717             }
82718
82719             section.entityIDs = function(val) {
82720                 if (!arguments.length) { return _entityIDs; }
82721                 if (!_entityIDs || !val || !utilArrayIdentical(_entityIDs, val)) {
82722                     _entityIDs = val;
82723                     _activeIssueID = null;
82724                     reloadIssues();
82725                 }
82726                 return section;
82727             };
82728
82729             return section;
82730         }
82731
82732         function uiPresetIcon() {
82733           var _preset;
82734           var _geometry;
82735           var _sizeClass = 'medium';
82736
82737
82738           function isSmall() {
82739             return _sizeClass === 'small';
82740           }
82741
82742
82743           function presetIcon(selection) {
82744             selection.each(render);
82745           }
82746
82747
82748           function getIcon(p, geom) {
82749             if (isSmall() && p.isFallback && p.isFallback())
82750               { return 'iD-icon-' + p.id; }
82751             else if (p.icon)
82752               { return p.icon; }
82753             else if (geom === 'line')
82754               { return 'iD-other-line'; }
82755             else if (geom === 'vertex')
82756               { return p.isFallback() ? '' : 'temaki-vertex'; }
82757             else if (isSmall() && geom === 'point')
82758               { return ''; }
82759             else
82760               { return 'maki-marker-stroked'; }
82761           }
82762
82763
82764           function renderPointBorder(enter) {
82765             var w = 40;
82766             var h = 40;
82767
82768             enter
82769               .append('svg')
82770               .attr('class', 'preset-icon-fill preset-icon-point-border')
82771               .attr('width', w)
82772               .attr('height', h)
82773               .attr('viewBox', ("0 0 " + w + " " + h))
82774               .append('path')
82775               .attr('transform', 'translate(11.5, 8)')
82776               .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');
82777           }
82778
82779
82780           function renderCircleFill(fillEnter) {
82781             var w = 60;
82782             var h = 60;
82783             var d = 40;
82784
82785             fillEnter
82786               .append('svg')
82787               .attr('class', 'preset-icon-fill preset-icon-fill-vertex')
82788               .attr('width', w)
82789               .attr('height', h)
82790               .attr('viewBox', ("0 0 " + w + " " + h))
82791               .append('circle')
82792               .attr('cx', w / 2)
82793               .attr('cy', h / 2)
82794               .attr('r', d / 2);
82795           }
82796
82797
82798           function renderSquareFill(fillEnter) {
82799             var d = isSmall() ? 40 : 60;
82800             var w = d;
82801             var h = d;
82802             var l = d * 2/3;
82803             var c1 = (w-l) / 2;
82804             var c2 = c1 + l;
82805
82806             fillEnter = fillEnter
82807               .append('svg')
82808               .attr('class', 'preset-icon-fill preset-icon-fill-area')
82809               .attr('width', w)
82810               .attr('height', h)
82811               .attr('viewBox', ("0 0 " + w + " " + h));
82812
82813             ['fill', 'stroke'].forEach(function (klass) {
82814               fillEnter
82815                 .append('path')
82816                 .attr('d', ("M" + c1 + " " + c1 + " L" + c1 + " " + c2 + " L" + c2 + " " + c2 + " L" + c2 + " " + c1 + " Z"))
82817                 .attr('class', ("line area " + klass));
82818             });
82819
82820             var rVertex = 2.5;
82821             [[c1, c1], [c1, c2], [c2, c2], [c2, c1]].forEach(function (point) {
82822               fillEnter
82823                 .append('circle')
82824                 .attr('class', 'vertex')
82825                 .attr('cx', point[0])
82826                 .attr('cy', point[1])
82827                 .attr('r', rVertex);
82828             });
82829
82830             if (!isSmall()) {
82831               var rMidpoint = 1.25;
82832               [[c1, w/2], [c2, w/2], [h/2, c1], [h/2, c2]].forEach(function (point) {
82833                 fillEnter
82834                   .append('circle')
82835                   .attr('class', 'midpoint')
82836                   .attr('cx', point[0])
82837                   .attr('cy', point[1])
82838                   .attr('r', rMidpoint);
82839               });
82840             }
82841           }
82842
82843
82844           function renderLine(lineEnter) {
82845             var d = isSmall() ? 40 : 60;
82846             // draw the line parametrically
82847             var w = d;
82848             var h = d;
82849             var y = Math.round(d * 0.72);
82850             var l = Math.round(d * 0.6);
82851             var r = 2.5;
82852             var x1 = (w - l) / 2;
82853             var x2 = x1 + l;
82854
82855             lineEnter = lineEnter
82856               .append('svg')
82857               .attr('class', 'preset-icon-line')
82858               .attr('width', w)
82859               .attr('height', h)
82860               .attr('viewBox', ("0 0 " + w + " " + h));
82861
82862             ['casing', 'stroke'].forEach(function (klass) {
82863               lineEnter
82864                 .append('path')
82865                 .attr('d', ("M" + x1 + " " + y + " L" + x2 + " " + y))
82866                 .attr('class', ("line " + klass));
82867             });
82868
82869             [[x1-1, y], [x2+1, y]].forEach(function (point) {
82870               lineEnter
82871                 .append('circle')
82872                 .attr('class', 'vertex')
82873                 .attr('cx', point[0])
82874                 .attr('cy', point[1])
82875                 .attr('r', r);
82876             });
82877           }
82878
82879
82880           function renderRoute(routeEnter) {
82881             var d = isSmall() ? 40 : 60;
82882             // draw the route parametrically
82883             var w = d;
82884             var h = d;
82885             var y1 = Math.round(d * 0.80);
82886             var y2 = Math.round(d * 0.68);
82887             var l = Math.round(d * 0.6);
82888             var r = 2;
82889             var x1 = (w - l) / 2;
82890             var x2 = x1 + l / 3;
82891             var x3 = x2 + l / 3;
82892             var x4 = x3 + l / 3;
82893
82894             routeEnter = routeEnter
82895               .append('svg')
82896               .attr('class', 'preset-icon-route')
82897               .attr('width', w)
82898               .attr('height', h)
82899               .attr('viewBox', ("0 0 " + w + " " + h));
82900
82901             ['casing', 'stroke'].forEach(function (klass) {
82902               routeEnter
82903                 .append('path')
82904                 .attr('d', ("M" + x1 + " " + y1 + " L" + x2 + " " + y2))
82905                 .attr('class', ("segment0 line " + klass));
82906               routeEnter
82907                 .append('path')
82908                 .attr('d', ("M" + x2 + " " + y2 + " L" + x3 + " " + y1))
82909                 .attr('class', ("segment1 line " + klass));
82910               routeEnter
82911                 .append('path')
82912                 .attr('d', ("M" + x3 + " " + y1 + " L" + x4 + " " + y2))
82913                 .attr('class', ("segment2 line " + klass));
82914             });
82915
82916             [[x1, y1], [x2, y2], [x3, y1], [x4, y2]].forEach(function (point) {
82917               routeEnter
82918                 .append('circle')
82919                 .attr('class', 'vertex')
82920                 .attr('cx', point[0])
82921                 .attr('cy', point[1])
82922                 .attr('r', r);
82923             });
82924           }
82925
82926
82927           // Route icons are drawn with a zigzag annotation underneath:
82928           //     o   o
82929           //    / \ /
82930           //   o   o
82931           // This dataset defines the styles that are used to draw the zigzag segments.
82932           var routeSegments = {
82933             bicycle: ['highway/cycleway', 'highway/cycleway', 'highway/cycleway'],
82934             bus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82935             trolleybus: ['highway/unclassified', 'highway/secondary', 'highway/primary'],
82936             detour: ['highway/tertiary', 'highway/residential', 'highway/unclassified'],
82937             ferry: ['route/ferry', 'route/ferry', 'route/ferry'],
82938             foot: ['highway/footway', 'highway/footway', 'highway/footway'],
82939             hiking: ['highway/path', 'highway/path', 'highway/path'],
82940             horse: ['highway/bridleway', 'highway/bridleway', 'highway/bridleway'],
82941             light_rail: ['railway/light_rail', 'railway/light_rail', 'railway/light_rail'],
82942             monorail: ['railway/monorail', 'railway/monorail', 'railway/monorail'],
82943             pipeline: ['man_made/pipeline', 'man_made/pipeline', 'man_made/pipeline'],
82944             piste: ['piste/downhill', 'piste/hike', 'piste/nordic'],
82945             power: ['power/line', 'power/line', 'power/line'],
82946             road: ['highway/secondary', 'highway/primary', 'highway/trunk'],
82947             subway: ['railway/subway', 'railway/subway', 'railway/subway'],
82948             train: ['railway/rail', 'railway/rail', 'railway/rail'],
82949             tram: ['railway/tram', 'railway/tram', 'railway/tram'],
82950             waterway: ['waterway/stream', 'waterway/stream', 'waterway/stream']
82951           };
82952
82953
82954           function render() {
82955             var p = _preset.apply(this, arguments);
82956             var geom = _geometry ? _geometry.apply(this, arguments) : null;
82957             if (geom === 'relation' && p.tags && ((p.tags.type === 'route' && p.tags.route && routeSegments[p.tags.route]) || p.tags.type === 'waterway')) {
82958               geom = 'route';
82959             }
82960
82961             var showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
82962             var isFallback = isSmall() && p.isFallback && p.isFallback();
82963             var imageURL = (showThirdPartyIcons === 'true') && p.imageURL;
82964             var picon = getIcon(p, geom);
82965             var isMaki = picon && /^maki-/.test(picon);
82966             var isTemaki = picon && /^temaki-/.test(picon);
82967             var isFa = picon && /^fa[srb]-/.test(picon);
82968             var isTnp = picon && /^tnp-/.test(picon);
82969             var isiDIcon = picon && !(isMaki || isTemaki || isFa || isTnp);
82970             var isCategory = !p.setTags;
82971             var drawPoint = picon && geom === 'point' && isSmall() && !isFallback;
82972             var drawVertex = picon !== null && geom === 'vertex' && (!isSmall() || !isFallback);
82973             var drawLine = picon && geom === 'line' && !isFallback && !isCategory;
82974             var drawArea = picon && geom === 'area' && !isFallback;
82975             var drawRoute = picon && geom === 'route';
82976             var isFramed = (drawVertex || drawArea || drawLine || drawRoute);
82977
82978             var tags = !isCategory ? p.setTags({}, geom) : {};
82979             for (var k in tags) {
82980               if (tags[k] === '*') {
82981                 tags[k] = 'yes';
82982               }
82983             }
82984
82985             var tagClasses = svgTagClasses().getClassesString(tags, '');
82986             var selection = select(this);
82987
82988             var container = selection.selectAll('.preset-icon-container')
82989               .data([0]);
82990
82991             container = container.enter()
82992               .append('div')
82993               .attr('class', ("preset-icon-container " + _sizeClass))
82994               .merge(container);
82995
82996             container
82997               .classed('showing-img', !!imageURL)
82998               .classed('fallback', isFallback);
82999
83000
83001             var pointBorder = container.selectAll('.preset-icon-point-border')
83002               .data(drawPoint ? [0] : []);
83003
83004             pointBorder.exit()
83005               .remove();
83006
83007             var pointBorderEnter = pointBorder.enter();
83008             renderPointBorder(pointBorderEnter);
83009             pointBorder = pointBorderEnter.merge(pointBorder);
83010
83011
83012             var vertexFill = container.selectAll('.preset-icon-fill-vertex')
83013               .data(drawVertex ? [0] : []);
83014
83015             vertexFill.exit()
83016               .remove();
83017
83018             var vertexFillEnter = vertexFill.enter();
83019             renderCircleFill(vertexFillEnter);
83020             vertexFill = vertexFillEnter.merge(vertexFill);
83021
83022
83023             var fill = container.selectAll('.preset-icon-fill-area')
83024               .data(drawArea ? [0] : []);
83025
83026             fill.exit()
83027               .remove();
83028
83029             var fillEnter = fill.enter();
83030             renderSquareFill(fillEnter);
83031             fill = fillEnter.merge(fill);
83032
83033             fill.selectAll('path.stroke')
83034               .attr('class', ("area stroke " + tagClasses));
83035             fill.selectAll('path.fill')
83036               .attr('class', ("area fill " + tagClasses));
83037
83038
83039             var line = container.selectAll('.preset-icon-line')
83040               .data(drawLine ? [0] : []);
83041
83042             line.exit()
83043               .remove();
83044
83045             var lineEnter = line.enter();
83046             renderLine(lineEnter);
83047             line = lineEnter.merge(line);
83048
83049             line.selectAll('path.stroke')
83050               .attr('class', ("line stroke " + tagClasses));
83051             line.selectAll('path.casing')
83052               .attr('class', ("line casing " + tagClasses));
83053
83054
83055             var route = container.selectAll('.preset-icon-route')
83056               .data(drawRoute ? [0] : []);
83057
83058             route.exit()
83059               .remove();
83060
83061             var routeEnter = route.enter();
83062             renderRoute(routeEnter);
83063             route = routeEnter.merge(route);
83064
83065             if (drawRoute) {
83066               var routeType = p.tags.type === 'waterway' ? 'waterway' : p.tags.route;
83067               var segmentPresetIDs = routeSegments[routeType];
83068               for (var i in segmentPresetIDs) {
83069                 var segmentPreset = _mainPresetIndex.item(segmentPresetIDs[i]);
83070                 var segmentTagClasses = svgTagClasses().getClassesString(segmentPreset.tags, '');
83071                 route.selectAll(("path.stroke.segment" + i))
83072                   .attr('class', ("segment" + i + " line stroke " + segmentTagClasses));
83073                 route.selectAll(("path.casing.segment" + i))
83074                   .attr('class', ("segment" + i + " line casing " + segmentTagClasses));
83075               }
83076             }
83077
83078
83079             var icon = container.selectAll('.preset-icon')
83080               .data(picon ? [0] : []);
83081
83082             icon.exit()
83083               .remove();
83084
83085             icon = icon.enter()
83086               .append('div')
83087               .attr('class', 'preset-icon')
83088               .call(svgIcon(''))
83089               .merge(icon);
83090
83091             icon
83092               .attr('class', 'preset-icon ' + (geom ? geom + '-geom' : ''))
83093               .classed('framed', isFramed)
83094               .classed('preset-icon-iD', isiDIcon);
83095
83096             icon.selectAll('svg')
83097               .attr('class', 'icon ' + picon + ' ' + (!isiDIcon && geom !== 'line'  ? '' : tagClasses));
83098
83099             icon.selectAll('use')
83100               .attr('href', '#' + picon + (isMaki ? (isSmall() && geom === 'point' ? '-11' : '-15') : ''));
83101
83102             var imageIcon = container.selectAll('img.image-icon')
83103               .data(imageURL ? [0] : []);
83104
83105             imageIcon.exit()
83106               .remove();
83107
83108             imageIcon = imageIcon.enter()
83109               .append('img')
83110               .attr('class', 'image-icon')
83111               .on('load', function () { return container.classed('showing-img', true); } )
83112               .on('error', function () { return container.classed('showing-img', false); } )
83113               .merge(imageIcon);
83114
83115             imageIcon
83116               .attr('src', imageURL);
83117           }
83118
83119
83120           presetIcon.preset = function(val) {
83121             if (!arguments.length) { return _preset; }
83122             _preset = utilFunctor(val);
83123             return presetIcon;
83124           };
83125
83126
83127           presetIcon.geometry = function(val) {
83128             if (!arguments.length) { return _geometry; }
83129             _geometry = utilFunctor(val);
83130             return presetIcon;
83131           };
83132
83133
83134           presetIcon.sizeClass = function(val) {
83135             if (!arguments.length) { return _sizeClass; }
83136             _sizeClass = val;
83137             return presetIcon;
83138           };
83139
83140           return presetIcon;
83141         }
83142
83143         function uiSectionFeatureType(context) {
83144
83145             var dispatch$1 = dispatch('choose');
83146
83147             var _entityIDs = [];
83148             var _presets = [];
83149
83150             var _tagReference;
83151
83152             var section = uiSection('feature-type', context)
83153                 .title(_t('inspector.feature_type'))
83154                 .disclosureContent(renderDisclosureContent);
83155
83156             function renderDisclosureContent(selection) {
83157
83158                 selection.classed('preset-list-item', true);
83159                 selection.classed('mixed-types', _presets.length > 1);
83160
83161                 var presetButtonWrap = selection
83162                     .selectAll('.preset-list-button-wrap')
83163                     .data([0])
83164                     .enter()
83165                     .append('div')
83166                     .attr('class', 'preset-list-button-wrap');
83167
83168                 var presetButton = presetButtonWrap
83169                     .append('button')
83170                     .attr('class', 'preset-list-button preset-reset')
83171                     .call(uiTooltip()
83172                         .title(_t('inspector.back_tooltip'))
83173                         .placement('bottom')
83174                     );
83175
83176                 presetButton.append('div')
83177                     .attr('class', 'preset-icon-container');
83178
83179                 presetButton
83180                     .append('div')
83181                     .attr('class', 'label')
83182                     .append('div')
83183                     .attr('class', 'label-inner');
83184
83185                 presetButtonWrap.append('div')
83186                     .attr('class', 'accessory-buttons');
83187
83188                 var tagReferenceBodyWrap = selection
83189                     .selectAll('.tag-reference-body-wrap')
83190                     .data([0]);
83191
83192                 tagReferenceBodyWrap = tagReferenceBodyWrap
83193                     .enter()
83194                     .append('div')
83195                     .attr('class', 'tag-reference-body-wrap')
83196                     .merge(tagReferenceBodyWrap);
83197
83198                 // update header
83199                 if (_tagReference) {
83200                     selection.selectAll('.preset-list-button-wrap .accessory-buttons')
83201                         .style('display', _presets.length === 1 ? null : 'none')
83202                         .call(_tagReference.button);
83203
83204                     tagReferenceBodyWrap
83205                         .style('display', _presets.length === 1 ? null : 'none')
83206                         .call(_tagReference.body);
83207                 }
83208
83209                 selection.selectAll('.preset-reset')
83210                     .on('click', function() {
83211                          dispatch$1.call('choose', this, _presets);
83212                     })
83213                     .on('pointerdown pointerup mousedown mouseup', function() {
83214                         event.preventDefault();
83215                         event.stopPropagation();
83216                     });
83217
83218                 var geometries = entityGeometries();
83219                 selection.select('.preset-list-item button')
83220                     .call(uiPresetIcon()
83221                         .geometry(_presets.length === 1 ? (geometries.length === 1 && geometries[0]) : null)
83222                         .preset(_presets.length === 1 ? _presets[0] : _mainPresetIndex.item('point'))
83223                     );
83224
83225                 // NOTE: split on en-dash, not a hypen (to avoid conflict with hyphenated names)
83226                 var names = _presets.length === 1 ? _presets[0].name().split(' – ') : [_t('inspector.multiple_types')];
83227
83228                 var label = selection.select('.label-inner');
83229                 var nameparts = label.selectAll('.namepart')
83230                     .data(names, function(d) { return d; });
83231
83232                 nameparts.exit()
83233                     .remove();
83234
83235                 nameparts
83236                     .enter()
83237                     .append('div')
83238                     .attr('class', 'namepart')
83239                     .text(function(d) { return d; });
83240             }
83241
83242             section.entityIDs = function(val) {
83243                 if (!arguments.length) { return _entityIDs; }
83244                 _entityIDs = val;
83245                 return section;
83246             };
83247
83248             section.presets = function(val) {
83249                 if (!arguments.length) { return _presets; }
83250
83251                 // don't reload the same preset
83252                 if (!utilArrayIdentical(val, _presets)) {
83253                     _presets = val;
83254
83255                     var geometries = entityGeometries();
83256                     if (_presets.length === 1 && geometries.length) {
83257                         _tagReference = uiTagReference(_presets[0].reference(geometries[0]))
83258                             .showing(false);
83259                     }
83260                 }
83261
83262                 return section;
83263             };
83264
83265             function entityGeometries() {
83266
83267                 var counts = {};
83268
83269                 for (var i in _entityIDs) {
83270                     var geometry = context.graph().geometry(_entityIDs[i]);
83271                     if (!counts[geometry]) { counts[geometry] = 0; }
83272                     counts[geometry] += 1;
83273                 }
83274
83275                 return Object.keys(counts).sort(function(geom1, geom2) {
83276                     return counts[geom2] - counts[geom1];
83277                 });
83278             }
83279
83280             return utilRebind(section, dispatch$1, 'on');
83281         }
83282
83283         // This currently only works with the 'restrictions' field
83284         // It borrows some code from uiHelp
83285
83286         function uiFieldHelp(context, fieldName) {
83287             var fieldHelp = {};
83288             var _inspector = select(null);
83289             var _wrap = select(null);
83290             var _body = select(null);
83291
83292             var fieldHelpKeys = {
83293                 restrictions: [
83294                     ['about',[
83295                         'about',
83296                         'from_via_to',
83297                         'maxdist',
83298                         'maxvia'
83299                     ]],
83300                     ['inspecting',[
83301                         'about',
83302                         'from_shadow',
83303                         'allow_shadow',
83304                         'restrict_shadow',
83305                         'only_shadow',
83306                         'restricted',
83307                         'only'
83308                     ]],
83309                     ['modifying',[
83310                         'about',
83311                         'indicators',
83312                         'allow_turn',
83313                         'restrict_turn',
83314                         'only_turn'
83315                     ]],
83316                     ['tips',[
83317                         'simple',
83318                         'simple_example',
83319                         'indirect',
83320                         'indirect_example',
83321                         'indirect_noedit'
83322                     ]]
83323                 ]
83324             };
83325
83326             var fieldHelpHeadings = {};
83327
83328             var replacements = {
83329                 distField: _t('restriction.controls.distance'),
83330                 viaField: _t('restriction.controls.via'),
83331                 fromShadow: icon('#iD-turn-shadow', 'pre-text shadow from'),
83332                 allowShadow: icon('#iD-turn-shadow', 'pre-text shadow allow'),
83333                 restrictShadow: icon('#iD-turn-shadow', 'pre-text shadow restrict'),
83334                 onlyShadow: icon('#iD-turn-shadow', 'pre-text shadow only'),
83335                 allowTurn: icon('#iD-turn-yes', 'pre-text turn'),
83336                 restrictTurn: icon('#iD-turn-no', 'pre-text turn'),
83337                 onlyTurn: icon('#iD-turn-only', 'pre-text turn')
83338             };
83339
83340
83341             // For each section, squash all the texts into a single markdown document
83342             var docs = fieldHelpKeys[fieldName].map(function(key) {
83343                 var helpkey = 'help.field.' + fieldName + '.' + key[0];
83344                 var text = key[1].reduce(function(all, part) {
83345                     var subkey = helpkey + '.' + part;
83346                     var depth = fieldHelpHeadings[subkey];                     // is this subkey a heading?
83347                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
83348                     return all + hhh + _t(subkey, replacements) + '\n\n';
83349                 }, '');
83350
83351                 return {
83352                     key: helpkey,
83353                     title: _t(helpkey + '.title'),
83354                     html: marked_1(text.trim())
83355                 };
83356             });
83357
83358
83359             function show() {
83360                 updatePosition();
83361
83362                 _body
83363                     .classed('hide', false)
83364                     .style('opacity', '0')
83365                     .transition()
83366                     .duration(200)
83367                     .style('opacity', '1');
83368             }
83369
83370
83371             function hide() {
83372                 _body
83373                     .classed('hide', true)
83374                     .transition()
83375                     .duration(200)
83376                     .style('opacity', '0')
83377                     .on('end', function () {
83378                         _body.classed('hide', true);
83379                     });
83380             }
83381
83382
83383             function clickHelp(index) {
83384                 var d = docs[index];
83385                 var tkeys = fieldHelpKeys[fieldName][index][1];
83386
83387                 _body.selectAll('.field-help-nav-item')
83388                     .classed('active', function(d, i) { return i === index; });
83389
83390                 var content = _body.selectAll('.field-help-content')
83391                     .html(d.html);
83392
83393                 // class the paragraphs so we can find and style them
83394                 content.selectAll('p')
83395                     .attr('class', function(d, i) { return tkeys[i]; });
83396
83397                 // insert special content for certain help sections
83398                 if (d.key === 'help.field.restrictions.inspecting') {
83399                     content
83400                         .insert('img', 'p.from_shadow')
83401                         .attr('class', 'field-help-image cf')
83402                         .attr('src', context.imagePath('tr_inspect.gif'));
83403
83404                 } else if (d.key === 'help.field.restrictions.modifying') {
83405                     content
83406                         .insert('img', 'p.allow_turn')
83407                         .attr('class', 'field-help-image cf')
83408                         .attr('src', context.imagePath('tr_modify.gif'));
83409                 }
83410             }
83411
83412
83413             fieldHelp.button = function(selection) {
83414                 if (_body.empty()) { return; }
83415
83416                 var button = selection.selectAll('.field-help-button')
83417                     .data([0]);
83418
83419                 // enter/update
83420                 button.enter()
83421                     .append('button')
83422                     .attr('class', 'field-help-button')
83423                     .attr('tabindex', -1)
83424                     .call(svgIcon('#iD-icon-help'))
83425                     .merge(button)
83426                     .on('click', function () {
83427                         event.stopPropagation();
83428                         event.preventDefault();
83429                         if (_body.classed('hide')) {
83430                             show();
83431                         } else {
83432                             hide();
83433                         }
83434                     });
83435             };
83436
83437
83438             function updatePosition() {
83439                 var wrap = _wrap.node();
83440                 var inspector = _inspector.node();
83441                 var wRect = wrap.getBoundingClientRect();
83442                 var iRect = inspector.getBoundingClientRect();
83443
83444                 _body
83445                     .style('top', wRect.top + inspector.scrollTop - iRect.top + 'px');
83446             }
83447
83448
83449             fieldHelp.body = function(selection) {
83450                 // This control expects the field to have a form-field-input-wrap div
83451                 _wrap = selection.selectAll('.form-field-input-wrap');
83452                 if (_wrap.empty()) { return; }
83453
83454                 // absolute position relative to the inspector, so it "floats" above the fields
83455                 _inspector = context.container().select('.sidebar .entity-editor-pane .inspector-body');
83456                 if (_inspector.empty()) { return; }
83457
83458                 _body = _inspector.selectAll('.field-help-body')
83459                     .data([0]);
83460
83461                 var enter = _body.enter()
83462                     .append('div')
83463                     .attr('class', 'field-help-body hide');   // initially hidden
83464
83465                 var titleEnter = enter
83466                     .append('div')
83467                     .attr('class', 'field-help-title cf');
83468
83469                 titleEnter
83470                     .append('h2')
83471                     .attr('class', ((_mainLocalizer.textDirection() === 'rtl') ? 'fr' : 'fl'))
83472                     .text(_t('help.field.' + fieldName + '.title'));
83473
83474                 titleEnter
83475                     .append('button')
83476                     .attr('class', 'fr close')
83477                     .on('click', function() {
83478                         event.stopPropagation();
83479                         event.preventDefault();
83480                         hide();
83481                     })
83482                     .call(svgIcon('#iD-icon-close'));
83483
83484                 var navEnter = enter
83485                     .append('div')
83486                     .attr('class', 'field-help-nav cf');
83487
83488                 var titles = docs.map(function(d) { return d.title; });
83489                 navEnter.selectAll('.field-help-nav-item')
83490                     .data(titles)
83491                     .enter()
83492                     .append('div')
83493                     .attr('class', 'field-help-nav-item')
83494                     .text(function(d) { return d; })
83495                     .on('click', function(d, i) {
83496                         event.stopPropagation();
83497                         event.preventDefault();
83498                         clickHelp(i);
83499                     });
83500
83501                 enter
83502                     .append('div')
83503                     .attr('class', 'field-help-content');
83504
83505                 _body = _body
83506                     .merge(enter);
83507
83508                 clickHelp(0);
83509             };
83510
83511
83512             return fieldHelp;
83513         }
83514
83515         function uiFieldCheck(field, context) {
83516             var dispatch$1 = dispatch('change');
83517             var options = field.strings && field.strings.options;
83518             var values = [];
83519             var texts = [];
83520
83521             var _tags;
83522
83523             var input = select(null);
83524             var text = select(null);
83525             var label = select(null);
83526             var reverser = select(null);
83527
83528             var _impliedYes;
83529             var _entityIDs = [];
83530             var _value;
83531
83532
83533             if (options) {
83534                 for (var k in options) {
83535                     values.push(k === 'undefined' ? undefined : k);
83536                     texts.push(field.t('options.' + k, { 'default': options[k] }));
83537                 }
83538             } else {
83539                 values = [undefined, 'yes'];
83540                 texts = [_t('inspector.unknown'), _t('inspector.check.yes')];
83541                 if (field.type !== 'defaultCheck') {
83542                     values.push('no');
83543                     texts.push(_t('inspector.check.no'));
83544                 }
83545             }
83546
83547
83548             // Checks tags to see whether an undefined value is "Assumed to be Yes"
83549             function checkImpliedYes() {
83550                 _impliedYes = (field.id === 'oneway_yes');
83551
83552                 // hack: pretend `oneway` field is a `oneway_yes` field
83553                 // where implied oneway tag exists (e.g. `junction=roundabout`) #2220, #1841
83554                 if (field.id === 'oneway') {
83555                     var entity = context.entity(_entityIDs[0]);
83556                     for (var key in entity.tags) {
83557                         if (key in osmOneWayTags && (entity.tags[key] in osmOneWayTags[key])) {
83558                             _impliedYes = true;
83559                             texts[0] = _t('presets.fields.oneway_yes.options.undefined');
83560                             break;
83561                         }
83562                     }
83563                 }
83564             }
83565
83566
83567             function reverserHidden() {
83568                 if (!context.container().select('div.inspector-hover').empty()) { return true; }
83569                 return !(_value === 'yes' || (_impliedYes && !_value));
83570             }
83571
83572
83573             function reverserSetText(selection) {
83574                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
83575                 if (reverserHidden() || !entity) { return selection; }
83576
83577                 var first = entity.first();
83578                 var last = entity.isClosed() ? entity.nodes[entity.nodes.length - 2] : entity.last();
83579                 var pseudoDirection = first < last;
83580                 var icon = pseudoDirection ? '#iD-icon-forward' : '#iD-icon-backward';
83581
83582                 selection.selectAll('.reverser-span')
83583                     .text(_t('inspector.check.reverser'))
83584                     .call(svgIcon(icon, 'inline'));
83585
83586                 return selection;
83587             }
83588
83589
83590             var check = function(selection) {
83591                 checkImpliedYes();
83592
83593                 label = selection.selectAll('.form-field-input-wrap')
83594                     .data([0]);
83595
83596                 var enter = label.enter()
83597                     .append('label')
83598                     .attr('class', 'form-field-input-wrap form-field-input-check');
83599
83600                 enter
83601                     .append('input')
83602                     .property('indeterminate', field.type !== 'defaultCheck')
83603                     .attr('type', 'checkbox')
83604                     .attr('id', field.domId);
83605
83606                 enter
83607                     .append('span')
83608                     .text(texts[0])
83609                     .attr('class', 'value');
83610
83611                 if (field.type === 'onewayCheck') {
83612                     enter
83613                         .append('a')
83614                         .attr('class', 'reverser button' + (reverserHidden() ? ' hide' : ''))
83615                         .attr('href', '#')
83616                         .append('span')
83617                         .attr('class', 'reverser-span');
83618                 }
83619
83620                 label = label.merge(enter);
83621                 input = label.selectAll('input');
83622                 text = label.selectAll('span.value');
83623
83624                 input
83625                     .on('click', function() {
83626                         event.stopPropagation();
83627                         var t = {};
83628
83629                         if (Array.isArray(_tags[field.key])) {
83630                             if (values.indexOf('yes') !== -1) {
83631                                 t[field.key] = 'yes';
83632                             } else {
83633                                 t[field.key] = values[0];
83634                             }
83635                         } else {
83636                             t[field.key] = values[(values.indexOf(_value) + 1) % values.length];
83637                         }
83638
83639                         // Don't cycle through `alternating` or `reversible` states - #4970
83640                         // (They are supported as translated strings, but should not toggle with clicks)
83641                         if (t[field.key] === 'reversible' || t[field.key] === 'alternating') {
83642                             t[field.key] = values[0];
83643                         }
83644
83645                         dispatch$1.call('change', this, t);
83646                     });
83647
83648                 if (field.type === 'onewayCheck') {
83649                     reverser = label.selectAll('.reverser');
83650
83651                     reverser
83652                         .call(reverserSetText)
83653                         .on('click', function() {
83654                             event.preventDefault();
83655                             event.stopPropagation();
83656                             context.perform(
83657                                 function(graph) {
83658                                     for (var i in _entityIDs) {
83659                                         graph = actionReverse(_entityIDs[i])(graph);
83660                                     }
83661                                     return graph;
83662                                 },
83663                                 _t('operations.reverse.annotation')
83664                             );
83665
83666                             // must manually revalidate since no 'change' event was called
83667                             context.validator().validate();
83668
83669                             select(this)
83670                                 .call(reverserSetText);
83671                         });
83672                 }
83673             };
83674
83675
83676             check.entityIDs = function(val) {
83677                 if (!arguments.length) { return _entityIDs; }
83678                 _entityIDs = val;
83679                 return check;
83680             };
83681
83682
83683             check.tags = function(tags) {
83684
83685                 _tags = tags;
83686
83687                 function isChecked(val) {
83688                     return val !== 'no' && val !== '' && val !== undefined && val !== null;
83689                 }
83690
83691                 function textFor(val) {
83692                     if (val === '') { val = undefined; }
83693                     var index = values.indexOf(val);
83694                     return (index !== -1 ? texts[index] : ('"' + val + '"'));
83695                 }
83696
83697                 checkImpliedYes();
83698
83699                 var isMixed = Array.isArray(tags[field.key]);
83700
83701                 _value = !isMixed && tags[field.key] && tags[field.key].toLowerCase();
83702
83703                 if (field.type === 'onewayCheck' && (_value === '1' || _value === '-1')) {
83704                     _value = 'yes';
83705                 }
83706
83707                 input
83708                     .property('indeterminate', isMixed || (field.type !== 'defaultCheck' && !_value))
83709                     .property('checked', isChecked(_value));
83710
83711                 text
83712                     .text(isMixed ? _t('inspector.multiple_values') : textFor(_value))
83713                     .classed('mixed', isMixed);
83714
83715                 label
83716                     .classed('set', !!_value);
83717
83718                 if (field.type === 'onewayCheck') {
83719                     reverser
83720                         .classed('hide', reverserHidden())
83721                         .call(reverserSetText);
83722                 }
83723             };
83724
83725
83726             check.focus = function() {
83727                 input.node().focus();
83728             };
83729
83730             return utilRebind(check, dispatch$1, 'on');
83731         }
83732
83733         function uiFieldCombo(field, context) {
83734             var dispatch$1 = dispatch('change');
83735             var taginfo = services.taginfo;
83736             var isMulti = (field.type === 'multiCombo');
83737             var isNetwork = (field.type === 'networkCombo');
83738             var isSemi = (field.type === 'semiCombo');
83739             var optstrings = field.strings && field.strings.options;
83740             var optarray = field.options;
83741             var snake_case = (field.snake_case || (field.snake_case === undefined));
83742             var caseSensitive = field.caseSensitive;
83743             var combobox = uiCombobox(context, 'combo-' + field.safeid)
83744                 .caseSensitive(caseSensitive)
83745                 .minItems(isMulti || isSemi ? 1 : 2);
83746             var container = select(null);
83747             var inputWrap = select(null);
83748             var input = select(null);
83749             var _comboData = [];
83750             var _multiData = [];
83751             var _entityIDs = [];
83752             var _tags;
83753             var _countryCode;
83754             var _staticPlaceholder;
83755
83756             // initialize deprecated tags array
83757             var _dataDeprecated = [];
83758             _mainFileFetcher.get('deprecated')
83759                 .then(function(d) { _dataDeprecated = d; })
83760                 .catch(function() { /* ignore */ });
83761
83762
83763             // ensure multiCombo field.key ends with a ':'
83764             if (isMulti && /[^:]$/.test(field.key)) {
83765                 field.key += ':';
83766             }
83767
83768
83769             function snake(s) {
83770                 return s.replace(/\s+/g, '_');
83771             }
83772
83773             function unsnake(s) {
83774                 return s.replace(/_+/g, ' ');
83775             }
83776
83777             function clean(s) {
83778                 return s.split(';')
83779                     .map(function(s) { return s.trim(); })
83780                     .join(';');
83781             }
83782
83783
83784             // returns the tag value for a display value
83785             // (for multiCombo, dval should be the key suffix, not the entire key)
83786             function tagValue(dval) {
83787                 dval = clean(dval || '');
83788
83789                 if (optstrings) {
83790                     var found = _comboData.find(function(o) {
83791                         return o.key && clean(o.value) === dval;
83792                     });
83793                     if (found) {
83794                         return found.key;
83795                     }
83796                 }
83797
83798                 if (field.type === 'typeCombo' && !dval) {
83799                     return 'yes';
83800                 }
83801
83802                 return (snake_case ? snake(dval) : dval) || undefined;
83803             }
83804
83805
83806             // returns the display value for a tag value
83807             // (for multiCombo, tval should be the key suffix, not the entire key)
83808             function displayValue(tval) {
83809                 tval = tval || '';
83810
83811                 if (optstrings) {
83812                     var found = _comboData.find(function(o) {
83813                         return o.key === tval && o.value;
83814                     });
83815                     if (found) {
83816                         return found.value;
83817                     }
83818                 }
83819
83820                 if (field.type === 'typeCombo' && tval.toLowerCase() === 'yes') {
83821                     return '';
83822                 }
83823
83824                 return snake_case ? unsnake(tval) : tval;
83825             }
83826
83827
83828             // Compute the difference between arrays of objects by `value` property
83829             //
83830             // objectDifference([{value:1}, {value:2}, {value:3}], [{value:2}])
83831             // > [{value:1}, {value:3}]
83832             //
83833             function objectDifference(a, b) {
83834                 return a.filter(function(d1) {
83835                     return !b.some(function(d2) {
83836                         return !d2.isMixed && d1.value === d2.value;
83837                     });
83838                 });
83839             }
83840
83841
83842             function initCombo(selection, attachTo) {
83843                 if (optstrings) {
83844                     selection.attr('readonly', 'readonly');
83845                     selection.call(combobox, attachTo);
83846                     setStaticValues(setPlaceholder);
83847
83848                 } else if (optarray) {
83849                     selection.call(combobox, attachTo);
83850                     setStaticValues(setPlaceholder);
83851
83852                 } else if (taginfo) {
83853                     selection.call(combobox.fetcher(setTaginfoValues), attachTo);
83854                     setTaginfoValues('', setPlaceholder);
83855                 }
83856             }
83857
83858
83859             function setStaticValues(callback) {
83860                 if (!(optstrings || optarray)) { return; }
83861
83862                 if (optstrings) {
83863                     _comboData = Object.keys(optstrings).map(function(k) {
83864                         var v = field.t('options.' + k, { 'default': optstrings[k] });
83865                         return {
83866                             key: k,
83867                             value: v,
83868                             title: v
83869                         };
83870                     });
83871
83872                 } else if (optarray) {
83873                     _comboData = optarray.map(function(k) {
83874                         var v = snake_case ? unsnake(k) : k;
83875                         return {
83876                             key: k,
83877                             value: v,
83878                             title: v
83879                         };
83880                     });
83881                 }
83882
83883                 combobox.data(objectDifference(_comboData, _multiData));
83884                 if (callback) { callback(_comboData); }
83885             }
83886
83887
83888             function setTaginfoValues(q, callback) {
83889                 var fn = isMulti ? 'multikeys' : 'values';
83890                 var query = (isMulti ? field.key : '') + q;
83891                 var hasCountryPrefix = isNetwork && _countryCode && _countryCode.indexOf(q.toLowerCase()) === 0;
83892                 if (hasCountryPrefix) {
83893                     query = _countryCode + ':';
83894                 }
83895
83896                 var params = {
83897                     debounce: (q !== ''),
83898                     key: field.key,
83899                     query: query
83900                 };
83901
83902                 if (_entityIDs.length) {
83903                     params.geometry = context.graph().geometry(_entityIDs[0]);
83904                 }
83905
83906                 taginfo[fn](params, function(err, data) {
83907                     if (err) { return; }
83908
83909                     data = data.filter(function(d) {
83910
83911                         if (field.type === 'typeCombo' && d.value === 'yes') {
83912                             // don't show the fallback value
83913                             return false;
83914                         }
83915
83916                         // don't show values with very low usage
83917                         return !d.count || d.count > 10;
83918                     });
83919
83920                     var deprecatedValues = osmEntity.deprecatedTagValuesByKey(_dataDeprecated)[field.key];
83921                     if (deprecatedValues) {
83922                         // don't suggest deprecated tag values
83923                         data = data.filter(function(d) {
83924                             return deprecatedValues.indexOf(d.value) === -1;
83925                         });
83926                     }
83927
83928                     if (hasCountryPrefix) {
83929                         data = data.filter(function(d) {
83930                             return d.value.toLowerCase().indexOf(_countryCode + ':') === 0;
83931                         });
83932                     }
83933
83934                     // hide the caret if there are no suggestions
83935                     container.classed('empty-combobox', data.length === 0);
83936
83937                     _comboData = data.map(function(d) {
83938                         var k = d.value;
83939                         if (isMulti) { k = k.replace(field.key, ''); }
83940                         var v = snake_case ? unsnake(k) : k;
83941                         return {
83942                             key: k,
83943                             value: v,
83944                             title: isMulti ? v : d.title
83945                         };
83946                     });
83947
83948                     _comboData = objectDifference(_comboData, _multiData);
83949                     if (callback) { callback(_comboData); }
83950                 });
83951             }
83952
83953
83954             function setPlaceholder(values) {
83955
83956                 if (isMulti || isSemi) {
83957                     _staticPlaceholder = field.placeholder() || _t('inspector.add');
83958                 } else {
83959                     var vals = values
83960                         .map(function(d) { return d.value; })
83961                         .filter(function(s) { return s.length < 20; });
83962
83963                     var placeholders = vals.length > 1 ? vals : values.map(function(d) { return d.key; });
83964                     _staticPlaceholder = field.placeholder() || placeholders.slice(0, 3).join(', ');
83965                 }
83966
83967                 if (!/(…|\.\.\.)$/.test(_staticPlaceholder)) {
83968                     _staticPlaceholder += '…';
83969                 }
83970
83971                 var ph;
83972                 if (!isMulti && !isSemi && _tags && Array.isArray(_tags[field.key])) {
83973                     ph = _t('inspector.multiple_values');
83974                 } else {
83975                     ph =  _staticPlaceholder;
83976                 }
83977
83978                 container.selectAll('input')
83979                     .attr('placeholder', ph);
83980             }
83981
83982
83983             function change() {
83984                 var t = {};
83985                 var val;
83986
83987                 if (isMulti || isSemi) {
83988                     val = tagValue(utilGetSetValue(input).replace(/,/g, ';')) || '';
83989                     container.classed('active', false);
83990                     utilGetSetValue(input, '');
83991
83992                     var vals = val.split(';').filter(Boolean);
83993                     if (!vals.length) { return; }
83994
83995                     if (isMulti) {
83996                         utilArrayUniq(vals).forEach(function(v) {
83997                             var key = field.key + v;
83998                             if (_tags) {
83999                                 // don't set a multicombo value to 'yes' if it already has a non-'no' value
84000                                 // e.g. `language:de=main`
84001                                 var old = _tags[key];
84002                                 if (typeof old === 'string' && old.toLowerCase() !== 'no') { return; }
84003                             }
84004                             key = context.cleanTagKey(key);
84005                             field.keys.push(key);
84006                             t[key] = 'yes';
84007                         });
84008
84009                     } else if (isSemi) {
84010                         var arr = _multiData.map(function(d) { return d.key; });
84011                         arr = arr.concat(vals);
84012                         t[field.key] = context.cleanTagValue(utilArrayUniq(arr).filter(Boolean).join(';'));
84013                     }
84014
84015                     window.setTimeout(function() { input.node().focus(); }, 10);
84016
84017                 } else {
84018                     var rawValue = utilGetSetValue(input);
84019
84020                     // don't override multiple values with blank string
84021                     if (!rawValue && Array.isArray(_tags[field.key])) { return; }
84022
84023                     val = context.cleanTagValue(tagValue(rawValue));
84024                     t[field.key] = val || undefined;
84025                 }
84026
84027                 dispatch$1.call('change', this, t);
84028             }
84029
84030
84031             function removeMultikey(d) {
84032                 event.stopPropagation();
84033                 var t = {};
84034                 if (isMulti) {
84035                     t[d.key] = undefined;
84036                 } else if (isSemi) {
84037                     var arr = _multiData.map(function(md) {
84038                         return md.key === d.key ? null : md.key;
84039                     }).filter(Boolean);
84040
84041                     arr = utilArrayUniq(arr);
84042                     t[field.key] = arr.length ? arr.join(';') : undefined;
84043                 }
84044                 dispatch$1.call('change', this, t);
84045             }
84046
84047
84048             function combo(selection) {
84049                 container = selection.selectAll('.form-field-input-wrap')
84050                     .data([0]);
84051
84052                 var type = (isMulti || isSemi) ? 'multicombo': 'combo';
84053                 container = container.enter()
84054                     .append('div')
84055                     .attr('class', 'form-field-input-wrap form-field-input-' + type)
84056                     .merge(container);
84057
84058                 if (isMulti || isSemi) {
84059                     container = container.selectAll('.chiplist')
84060                         .data([0]);
84061
84062                     var listClass = 'chiplist';
84063
84064                     // Use a separate line for each value in the Destinations field
84065                     // to mimic highway exit signs
84066                     if (field.key === 'destination') {
84067                         listClass += ' full-line-chips';
84068                     }
84069
84070                     container = container.enter()
84071                         .append('ul')
84072                         .attr('class', listClass)
84073                         .on('click', function() {
84074                             window.setTimeout(function() { input.node().focus(); }, 10);
84075                         })
84076                         .merge(container);
84077
84078
84079                     inputWrap = container.selectAll('.input-wrap')
84080                         .data([0]);
84081
84082                     inputWrap = inputWrap.enter()
84083                         .append('li')
84084                         .attr('class', 'input-wrap')
84085                         .merge(inputWrap);
84086
84087                     input = inputWrap.selectAll('input')
84088                         .data([0]);
84089                 } else {
84090                     input = container.selectAll('input')
84091                         .data([0]);
84092                 }
84093
84094                 input = input.enter()
84095                     .append('input')
84096                     .attr('type', 'text')
84097                     .attr('id', field.domId)
84098                     .call(utilNoAuto)
84099                     .call(initCombo, selection)
84100                     .merge(input);
84101
84102                 if (isNetwork) {
84103                     var extent = combinedEntityExtent();
84104                     var countryCode = extent && iso1A2Code(extent.center());
84105                     _countryCode = countryCode && countryCode.toLowerCase();
84106                 }
84107
84108                 input
84109                     .on('change', change)
84110                     .on('blur', change);
84111
84112                 input
84113                     .on('keydown.field', function() {
84114                         switch (event.keyCode) {
84115                             case 13: // ↩ Return
84116                                 input.node().blur(); // blurring also enters the value
84117                                 event.stopPropagation();
84118                                 break;
84119                         }
84120                     });
84121
84122                 if (isMulti || isSemi) {
84123                     combobox
84124                         .on('accept', function() {
84125                             input.node().blur();
84126                             input.node().focus();
84127                         });
84128
84129                     input
84130                         .on('focus', function() { container.classed('active', true); });
84131                 }
84132             }
84133
84134
84135             combo.tags = function(tags) {
84136                 _tags = tags;
84137
84138                 if (isMulti || isSemi) {
84139                     _multiData = [];
84140
84141                     var maxLength;
84142
84143                     if (isMulti) {
84144                         // Build _multiData array containing keys already set..
84145                         for (var k in tags) {
84146                             if (k.indexOf(field.key) !== 0) { continue; }
84147                             var v = tags[k];
84148                             if (!v || (typeof v === 'string' && v.toLowerCase() === 'no')) { continue; }
84149
84150                             var suffix = k.substring(field.key.length);
84151                             _multiData.push({
84152                                 key: k,
84153                                 value: displayValue(suffix),
84154                                 isMixed: Array.isArray(v)
84155                             });
84156                         }
84157
84158                         // Set keys for form-field modified (needed for undo and reset buttons)..
84159                         field.keys = _multiData.map(function(d) { return d.key; });
84160
84161                         // limit the input length so it fits after prepending the key prefix
84162                         maxLength = context.maxCharsForTagKey() - utilUnicodeCharsCount(field.key);
84163
84164                     } else if (isSemi) {
84165
84166                         var allValues = [];
84167                         var commonValues;
84168                         if (Array.isArray(tags[field.key])) {
84169
84170                             tags[field.key].forEach(function(tagVal) {
84171                                 var thisVals = utilArrayUniq((tagVal || '').split(';')).filter(Boolean);
84172                                 allValues = allValues.concat(thisVals);
84173                                 if (!commonValues) {
84174                                     commonValues = thisVals;
84175                                 } else {
84176                                     commonValues = commonValues.filter(function (value) { return thisVals.includes(value); });
84177                                 }
84178                             });
84179                             allValues = utilArrayUniq(allValues).filter(Boolean);
84180
84181                         } else {
84182                             allValues =  utilArrayUniq((tags[field.key] || '').split(';')).filter(Boolean);
84183                             commonValues = allValues;
84184                         }
84185
84186                         _multiData = allValues.map(function(v) {
84187                             return {
84188                                 key: v,
84189                                 value: displayValue(v),
84190                                 isMixed: !commonValues.includes(v)
84191                             };
84192                         });
84193
84194                         var currLength = utilUnicodeCharsCount(commonValues.join(';'));
84195
84196                         // limit the input length to the remaining available characters
84197                         maxLength = context.maxCharsForTagValue() - currLength;
84198
84199                         if (currLength > 0) {
84200                             // account for the separator if a new value will be appended to existing
84201                             maxLength -= 1;
84202                         }
84203                     }
84204                     // a negative maxlength doesn't make sense
84205                     maxLength = Math.max(0, maxLength);
84206
84207                     var allowDragAndDrop = isSemi // only semiCombo values are ordered
84208                         && !Array.isArray(tags[field.key]);
84209
84210                     // Exclude existing multikeys from combo options..
84211                     var available = objectDifference(_comboData, _multiData);
84212                     combobox.data(available);
84213
84214                     // Hide 'Add' button if this field uses fixed set of
84215                     // translateable optstrings and they're all currently used,
84216                     // or if the field is already at its character limit
84217                     var hideAdd = (optstrings && !available.length) || maxLength <= 0;
84218                     container.selectAll('.chiplist .input-wrap')
84219                         .style('display', hideAdd ? 'none' : null);
84220
84221
84222                     // Render chips
84223                     var chips = container.selectAll('.chip')
84224                         .data(_multiData);
84225
84226                     chips.exit()
84227                         .remove();
84228
84229                     var enter = chips.enter()
84230                         .insert('li', '.input-wrap')
84231                         .attr('class', 'chip');
84232
84233                     enter.append('span');
84234                     enter.append('a');
84235
84236                     chips = chips.merge(enter)
84237                         .order()
84238                         .classed('draggable', allowDragAndDrop)
84239                         .classed('mixed', function(d) {
84240                             return d.isMixed;
84241                         })
84242                         .attr('title', function(d) {
84243                             return d.isMixed ? _t('inspector.unshared_value_tooltip') : null;
84244                         });
84245
84246                     if (allowDragAndDrop) {
84247                         registerDragAndDrop(chips);
84248                     }
84249
84250                     chips.select('span')
84251                         .text(function(d) { return d.value; });
84252
84253                     chips.select('a')
84254                         .on('click', removeMultikey)
84255                         .attr('class', 'remove')
84256                         .text('×');
84257
84258                 } else {
84259                     var isMixed = Array.isArray(tags[field.key]);
84260
84261                     var mixedValues = isMixed && tags[field.key].map(function(val) {
84262                         return displayValue(val);
84263                     }).filter(Boolean);
84264
84265                     utilGetSetValue(input, !isMixed ? displayValue(tags[field.key]) : '')
84266                         .attr('title', isMixed ? mixedValues.join('\n') : undefined)
84267                         .attr('placeholder', isMixed ? _t('inspector.multiple_values') : _staticPlaceholder || '')
84268                         .classed('mixed', isMixed);
84269                 }
84270             };
84271
84272             function registerDragAndDrop(selection) {
84273
84274                 // allow drag and drop re-ordering of chips
84275                 var dragOrigin, targetIndex;
84276                 selection.call(d3_drag()
84277                     .on('start', function() {
84278                         dragOrigin = {
84279                             x: event.x,
84280                             y: event.y
84281                         };
84282                         targetIndex = null;
84283                     })
84284                     .on('drag', function(d, index) {
84285                         var x = event.x - dragOrigin.x,
84286                             y = event.y - dragOrigin.y;
84287
84288                         if (!select(this).classed('dragging') &&
84289                             // don't display drag until dragging beyond a distance threshold
84290                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
84291
84292                         select(this)
84293                             .classed('dragging', true);
84294
84295                         targetIndex = null;
84296                         var targetIndexOffsetTop = null;
84297                         var draggedTagWidth = select(this).node().offsetWidth;
84298
84299                         if (field.key === 'destination') { // meaning tags are full width
84300                             container.selectAll('.chip')
84301                                 .style('transform', function(d2, index2) {
84302                                     var node = select(this).node();
84303
84304                                     if (index === index2) {
84305                                         return 'translate(' + x + 'px, ' + y + 'px)';
84306                                     // move the dragged tag up the order
84307                                     } else if (index2 > index && event.y > node.offsetTop) {
84308                                         if (targetIndex === null || index2 > targetIndex) {
84309                                             targetIndex = index2;
84310                                         }
84311                                         return 'translateY(-100%)';
84312                                     // move the dragged tag down the order
84313                                     } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
84314                                         if (targetIndex === null || index2 < targetIndex) {
84315                                             targetIndex = index2;
84316                                         }
84317                                         return 'translateY(100%)';
84318                                     }
84319                                     return null;
84320                                 });
84321                         } else {
84322                             container.selectAll('.chip')
84323                                 .each(function(d2, index2) {
84324                                     var node = select(this).node();
84325
84326                                     // check the cursor is in the bounding box
84327                                     if (
84328                                         index !== index2 &&
84329                                         event.x < node.offsetLeft + node.offsetWidth + 5 &&
84330                                         event.x > node.offsetLeft &&
84331                                         event.y < node.offsetTop + node.offsetHeight &&
84332                                         event.y > node.offsetTop
84333                                     ) {
84334                                         targetIndex = index2;
84335                                         targetIndexOffsetTop = node.offsetTop;
84336                                     }
84337                                 })
84338                                 .style('transform', function(d2, index2) {
84339                                     var node = select(this).node();
84340
84341                                     if (index === index2) {
84342                                         return 'translate(' + x + 'px, ' + y + 'px)';
84343                                     }
84344
84345                                     // only translate tags in the same row
84346                                     if (node.offsetTop === targetIndexOffsetTop) {
84347                                         if (index2 < index && index2 >= targetIndex) {
84348                                             return 'translateX(' + draggedTagWidth + 'px)';
84349                                         } else if (index2 > index && index2 <= targetIndex) {
84350                                             return 'translateX(-' + draggedTagWidth + 'px)';
84351                                         }
84352                                     }
84353                                     return null;
84354                                 });
84355                             }
84356                     })
84357                     .on('end', function(d, index) {
84358                         if (!select(this).classed('dragging')) {
84359                             return;
84360                         }
84361
84362                         select(this)
84363                             .classed('dragging', false);
84364
84365                         container.selectAll('.chip')
84366                             .style('transform', null);
84367
84368                         if (typeof targetIndex === 'number') {
84369                             var element = _multiData[index];
84370                             _multiData.splice(index, 1);
84371                             _multiData.splice(targetIndex, 0, element);
84372
84373                             var t = {};
84374
84375                             if (_multiData.length) {
84376                                 t[field.key] = _multiData.map(function(element) {
84377                                     return element.key;
84378                                 }).join(';');
84379                             } else {
84380                                 t[field.key] = undefined;
84381                             }
84382
84383                             dispatch$1.call('change', this, t);
84384                         }
84385                         dragOrigin = undefined;
84386                         targetIndex = undefined;
84387                     })
84388                 );
84389             }
84390
84391
84392             combo.focus = function() {
84393                 input.node().focus();
84394             };
84395
84396
84397             combo.entityIDs = function(val) {
84398                 if (!arguments.length) { return _entityIDs; }
84399                 _entityIDs = val;
84400                 return combo;
84401             };
84402
84403
84404             function combinedEntityExtent() {
84405                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84406             }
84407
84408
84409             return utilRebind(combo, dispatch$1, 'on');
84410         }
84411
84412         function uiFieldText(field, context) {
84413             var dispatch$1 = dispatch('change');
84414             var input = select(null);
84415             var outlinkButton = select(null);
84416             var _entityIDs = [];
84417             var _tags;
84418             var _phoneFormats = {};
84419
84420             if (field.type === 'tel') {
84421                 _mainFileFetcher.get('phone_formats')
84422                     .then(function(d) {
84423                         _phoneFormats = d;
84424                         updatePhonePlaceholder();
84425                     })
84426                     .catch(function() { /* ignore */ });
84427             }
84428
84429             function i(selection) {
84430                 var entity = _entityIDs.length && context.hasEntity(_entityIDs[0]);
84431                 var preset = entity && _mainPresetIndex.match(entity, context.graph());
84432                 var isLocked = preset && preset.suggestion && field.id === 'brand';
84433                 field.locked(isLocked);
84434
84435                 var wrap = selection.selectAll('.form-field-input-wrap')
84436                     .data([0]);
84437
84438                 wrap = wrap.enter()
84439                     .append('div')
84440                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84441                     .merge(wrap);
84442
84443                 input = wrap.selectAll('input')
84444                     .data([0]);
84445
84446                 input = input.enter()
84447                     .append('input')
84448                     .attr('type', field.type === 'identifier' ? 'text' : field.type)
84449                     .attr('id', field.domId)
84450                     .classed(field.type, true)
84451                     .call(utilNoAuto)
84452                     .merge(input);
84453
84454                 input
84455                     .classed('disabled', !!isLocked)
84456                     .attr('readonly', isLocked || null)
84457                     .on('input', change(true))
84458                     .on('blur', change())
84459                     .on('change', change());
84460
84461
84462                 if (field.type === 'tel') {
84463                     updatePhonePlaceholder();
84464
84465                 } else if (field.type === 'number') {
84466                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
84467
84468                     input.attr('type', 'text');
84469
84470                     var buttons = wrap.selectAll('.increment, .decrement')
84471                         .data(rtl ? [1, -1] : [-1, 1]);
84472
84473                     buttons.enter()
84474                         .append('button')
84475                         .attr('tabindex', -1)
84476                         .attr('class', function(d) {
84477                             var which = (d === 1 ? 'increment' : 'decrement');
84478                             return 'form-field-button ' + which;
84479                         })
84480                         .merge(buttons)
84481                         .on('click', function(d) {
84482                             event.preventDefault();
84483                             var raw_vals = input.node().value || '0';
84484                             var vals = raw_vals.split(';');
84485                             vals = vals.map(function(v) {
84486                                 var num = parseFloat(v.trim(), 10);
84487                                 return isFinite(num) ? clamped(num + d) : v.trim();
84488                             });
84489                             input.node().value = vals.join(';');
84490                             change()();
84491                         });
84492                 } else if (field.type === 'identifier' && field.urlFormat && field.pattern) {
84493
84494                     input.attr('type', 'text');
84495
84496                     outlinkButton = wrap.selectAll('.foreign-id-permalink')
84497                         .data([0]);
84498
84499                     outlinkButton.enter()
84500                         .append('button')
84501                         .attr('tabindex', -1)
84502                         .call(svgIcon('#iD-icon-out-link'))
84503                         .attr('class', 'form-field-button foreign-id-permalink')
84504                         .attr('title', function() {
84505                             var domainResults = /^https?:\/\/(.{1,}?)\//.exec(field.urlFormat);
84506                             if (domainResults.length >= 2 && domainResults[1]) {
84507                                 var domain = domainResults[1];
84508                                 return _t('icons.view_on', { domain: domain });
84509                             }
84510                             return '';
84511                         })
84512                         .on('click', function() {
84513                             event.preventDefault();
84514
84515                             var value = validIdentifierValueForLink();
84516                             if (value) {
84517                                 var url = field.urlFormat.replace(/{value}/, encodeURIComponent(value));
84518                                 window.open(url, '_blank');
84519                             }
84520                         })
84521                         .merge(outlinkButton);
84522                 }
84523             }
84524
84525
84526             function updatePhonePlaceholder() {
84527                 if (input.empty() || !Object.keys(_phoneFormats).length) { return; }
84528
84529                 var extent = combinedEntityExtent();
84530                 var countryCode = extent && iso1A2Code(extent.center());
84531                 var format = countryCode && _phoneFormats[countryCode.toLowerCase()];
84532                 if (format) { input.attr('placeholder', format); }
84533             }
84534
84535
84536             function validIdentifierValueForLink() {
84537                 if (field.type === 'identifier' && field.pattern) {
84538                     var value = utilGetSetValue(input).trim().split(';')[0];
84539                     return value && value.match(new RegExp(field.pattern));
84540                 }
84541                 return null;
84542             }
84543
84544
84545             // clamp number to min/max
84546             function clamped(num) {
84547                 if (field.minValue !== undefined) {
84548                     num = Math.max(num, field.minValue);
84549                 }
84550                 if (field.maxValue !== undefined) {
84551                     num = Math.min(num, field.maxValue);
84552                 }
84553                 return num;
84554             }
84555
84556
84557             function change(onInput) {
84558                 return function() {
84559                     var t = {};
84560                     var val = utilGetSetValue(input);
84561                     if (!onInput) { val = context.cleanTagValue(val); }
84562
84563                     // don't override multiple values with blank string
84564                     if (!val && Array.isArray(_tags[field.key])) { return; }
84565
84566                     if (!onInput) {
84567                         if (field.type === 'number' && val) {
84568                             var vals = val.split(';');
84569                             vals = vals.map(function(v) {
84570                                 var num = parseFloat(v.trim(), 10);
84571                                 return isFinite(num) ? clamped(num) : v.trim();
84572                             });
84573                             val = vals.join(';');
84574                         }
84575                         utilGetSetValue(input, val);
84576                     }
84577                     t[field.key] = val || undefined;
84578                     dispatch$1.call('change', this, t, onInput);
84579                 };
84580             }
84581
84582
84583             i.entityIDs = function(val) {
84584                 if (!arguments.length) { return _entityIDs; }
84585                 _entityIDs = val;
84586                 return i;
84587             };
84588
84589
84590             i.tags = function(tags) {
84591                 _tags = tags;
84592
84593                 var isMixed = Array.isArray(tags[field.key]);
84594
84595                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
84596                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
84597                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
84598                     .classed('mixed', isMixed);
84599
84600                 if (outlinkButton && !outlinkButton.empty()) {
84601                     var disabled = !validIdentifierValueForLink();
84602                     outlinkButton.classed('disabled', disabled);
84603                 }
84604             };
84605
84606
84607             i.focus = function() {
84608                 var node = input.node();
84609                 if (node) { node.focus(); }
84610             };
84611
84612             function combinedEntityExtent() {
84613                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
84614             }
84615
84616             return utilRebind(i, dispatch$1, 'on');
84617         }
84618
84619         function uiFieldAccess(field, context) {
84620             var dispatch$1 = dispatch('change');
84621             var items = select(null);
84622             var _tags;
84623
84624             function access(selection) {
84625                 var wrap = selection.selectAll('.form-field-input-wrap')
84626                     .data([0]);
84627
84628                 wrap = wrap.enter()
84629                     .append('div')
84630                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
84631                     .merge(wrap);
84632
84633                 var list = wrap.selectAll('ul')
84634                     .data([0]);
84635
84636                 list = list.enter()
84637                     .append('ul')
84638                     .attr('class', 'rows')
84639                     .merge(list);
84640
84641
84642                 items = list.selectAll('li')
84643                     .data(field.keys);
84644
84645                 // Enter
84646                 var enter = items.enter()
84647                     .append('li')
84648                     .attr('class', function(d) { return 'labeled-input preset-access-' + d; });
84649
84650                 enter
84651                     .append('span')
84652                     .attr('class', 'label preset-label-access')
84653                     .attr('for', function(d) { return 'preset-input-access-' + d; })
84654                     .text(function(d) { return field.t('types.' + d); });
84655
84656                 enter
84657                     .append('div')
84658                     .attr('class', 'preset-input-access-wrap')
84659                     .append('input')
84660                     .attr('type', 'text')
84661                     .attr('class', function(d) { return 'preset-input-access preset-input-access-' + d; })
84662                     .call(utilNoAuto)
84663                     .each(function(d) {
84664                         select(this)
84665                             .call(uiCombobox(context, 'access-' + d)
84666                                 .data(access.options(d))
84667                             );
84668                     });
84669
84670
84671                 // Update
84672                 items = items.merge(enter);
84673
84674                 wrap.selectAll('.preset-input-access')
84675                     .on('change', change)
84676                     .on('blur', change);
84677             }
84678
84679
84680             function change(d) {
84681                 var tag = {};
84682                 var value = context.cleanTagValue(utilGetSetValue(select(this)));
84683
84684                 // don't override multiple values with blank string
84685                 if (!value && typeof _tags[d] !== 'string') { return; }
84686
84687                 tag[d] = value || undefined;
84688                 dispatch$1.call('change', this, tag);
84689             }
84690
84691
84692             access.options = function(type) {
84693                 var options = ['no', 'permissive', 'private', 'permit', 'destination'];
84694
84695                 if (type !== 'access') {
84696                     options.unshift('yes');
84697                     options.push('designated');
84698
84699                     if (type === 'bicycle') {
84700                         options.push('dismount');
84701                     }
84702                 }
84703
84704                 return options.map(function(option) {
84705                     return {
84706                         title: field.t('options.' + option + '.description'),
84707                         value: option
84708                     };
84709                 });
84710             };
84711
84712
84713             var placeholdersByHighway = {
84714                 footway: {
84715                     foot: 'designated',
84716                     motor_vehicle: 'no'
84717                 },
84718                 steps: {
84719                     foot: 'yes',
84720                     motor_vehicle: 'no',
84721                     bicycle: 'no',
84722                     horse: 'no'
84723                 },
84724                 pedestrian: {
84725                     foot: 'yes',
84726                     motor_vehicle: 'no'
84727                 },
84728                 cycleway: {
84729                     motor_vehicle: 'no',
84730                     bicycle: 'designated'
84731                 },
84732                 bridleway: {
84733                     motor_vehicle: 'no',
84734                     horse: 'designated'
84735                 },
84736                 path: {
84737                     foot: 'yes',
84738                     motor_vehicle: 'no',
84739                     bicycle: 'yes',
84740                     horse: 'yes'
84741                 },
84742                 motorway: {
84743                     foot: 'no',
84744                     motor_vehicle: 'yes',
84745                     bicycle: 'no',
84746                     horse: 'no'
84747                 },
84748                 trunk: {
84749                     motor_vehicle: 'yes'
84750                 },
84751                 primary: {
84752                     foot: 'yes',
84753                     motor_vehicle: 'yes',
84754                     bicycle: 'yes',
84755                     horse: 'yes'
84756                 },
84757                 secondary: {
84758                     foot: 'yes',
84759                     motor_vehicle: 'yes',
84760                     bicycle: 'yes',
84761                     horse: 'yes'
84762                 },
84763                 tertiary: {
84764                     foot: 'yes',
84765                     motor_vehicle: 'yes',
84766                     bicycle: 'yes',
84767                     horse: 'yes'
84768                 },
84769                 residential: {
84770                     foot: 'yes',
84771                     motor_vehicle: 'yes',
84772                     bicycle: 'yes',
84773                     horse: 'yes'
84774                 },
84775                 unclassified: {
84776                     foot: 'yes',
84777                     motor_vehicle: 'yes',
84778                     bicycle: 'yes',
84779                     horse: 'yes'
84780                 },
84781                 service: {
84782                     foot: 'yes',
84783                     motor_vehicle: 'yes',
84784                     bicycle: 'yes',
84785                     horse: 'yes'
84786                 },
84787                 motorway_link: {
84788                     foot: 'no',
84789                     motor_vehicle: 'yes',
84790                     bicycle: 'no',
84791                     horse: 'no'
84792                 },
84793                 trunk_link: {
84794                     motor_vehicle: 'yes'
84795                 },
84796                 primary_link: {
84797                     foot: 'yes',
84798                     motor_vehicle: 'yes',
84799                     bicycle: 'yes',
84800                     horse: 'yes'
84801                 },
84802                 secondary_link: {
84803                     foot: 'yes',
84804                     motor_vehicle: 'yes',
84805                     bicycle: 'yes',
84806                     horse: 'yes'
84807                 },
84808                 tertiary_link: {
84809                     foot: 'yes',
84810                     motor_vehicle: 'yes',
84811                     bicycle: 'yes',
84812                     horse: 'yes'
84813                 }
84814             };
84815
84816
84817             access.tags = function(tags) {
84818                 _tags = tags;
84819
84820                 utilGetSetValue(items.selectAll('.preset-input-access'), function(d) {
84821                         return typeof tags[d] === 'string' ? tags[d] : '';
84822                     })
84823                     .classed('mixed', function(d) {
84824                         return tags[d] && Array.isArray(tags[d]);
84825                     })
84826                     .attr('title', function(d) {
84827                         return tags[d] && Array.isArray(tags[d]) && tags[d].filter(Boolean).join('\n');
84828                     })
84829                     .attr('placeholder', function(d) {
84830                         if (tags[d] && Array.isArray(tags[d])) {
84831                             return _t('inspector.multiple_values');
84832                         }
84833                         if (d === 'access') {
84834                             return 'yes';
84835                         }
84836                         if (tags.access && typeof tags.access === 'string') {
84837                             return tags.access;
84838                         }
84839                         if (tags.highway) {
84840                             if (typeof tags.highway === 'string') {
84841                                 if (placeholdersByHighway[tags.highway] &&
84842                                     placeholdersByHighway[tags.highway][d]) {
84843
84844                                     return placeholdersByHighway[tags.highway][d];
84845                                 }
84846                             } else {
84847                                 var impliedAccesses = tags.highway.filter(Boolean).map(function(highwayVal) {
84848                                     return placeholdersByHighway[highwayVal] && placeholdersByHighway[highwayVal][d];
84849                                 }).filter(Boolean);
84850
84851                                 if (impliedAccesses.length === tags.highway.length &&
84852                                     new Set(impliedAccesses).size === 1) {
84853                                     // if all the highway values have the same implied access for this type then use that
84854                                     return impliedAccesses[0];
84855                                 }
84856                             }
84857                         }
84858                         return field.placeholder();
84859                     });
84860             };
84861
84862
84863             access.focus = function() {
84864                 items.selectAll('.preset-input-access')
84865                     .node().focus();
84866             };
84867
84868
84869             return utilRebind(access, dispatch$1, 'on');
84870         }
84871
84872         function uiFieldAddress(field, context) {
84873             var dispatch$1 = dispatch('change');
84874             var _selection = select(null);
84875             var _wrap = select(null);
84876             var addrField = _mainPresetIndex.field('address');   // needed for placeholder strings
84877
84878             var _entityIDs = [];
84879             var _tags;
84880             var _countryCode;
84881             var _addressFormats = [{
84882                 format: [
84883                     ['housenumber', 'street'],
84884                     ['city', 'postcode']
84885                 ]
84886               }];
84887
84888             _mainFileFetcher.get('address_formats')
84889                 .then(function(d) {
84890                     _addressFormats = d;
84891                     if (!_selection.empty()) {
84892                         _selection.call(address);
84893                     }
84894                 })
84895                 .catch(function() { /* ignore */ });
84896
84897
84898             function getNearStreets() {
84899                 var extent = combinedEntityExtent();
84900                 var l = extent.center();
84901                 var box = geoExtent(l).padByMeters(200);
84902
84903                 var streets = context.history().intersects(box)
84904                     .filter(isAddressable)
84905                     .map(function(d) {
84906                         var loc = context.projection([
84907                             (extent[0][0] + extent[1][0]) / 2,
84908                             (extent[0][1] + extent[1][1]) / 2
84909                         ]);
84910                         var choice = geoChooseEdge(context.graph().childNodes(d), loc, context.projection);
84911
84912                         return {
84913                             title: d.tags.name,
84914                             value: d.tags.name,
84915                             dist: choice.distance
84916                         };
84917                     })
84918                     .sort(function(a, b) {
84919                         return a.dist - b.dist;
84920                     });
84921
84922                 return utilArrayUniqBy(streets, 'value');
84923
84924                 function isAddressable(d) {
84925                     return d.tags.highway && d.tags.name && d.type === 'way';
84926                 }
84927             }
84928
84929
84930             function getNearCities() {
84931                 var extent = combinedEntityExtent();
84932                 var l = extent.center();
84933                 var box = geoExtent(l).padByMeters(200);
84934
84935                 var cities = context.history().intersects(box)
84936                     .filter(isAddressable)
84937                     .map(function(d) {
84938                         return {
84939                             title: d.tags['addr:city'] || d.tags.name,
84940                             value: d.tags['addr:city'] || d.tags.name,
84941                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84942                         };
84943                     })
84944                     .sort(function(a, b) {
84945                         return a.dist - b.dist;
84946                     });
84947
84948                 return utilArrayUniqBy(cities, 'value');
84949
84950
84951                 function isAddressable(d) {
84952                     if (d.tags.name) {
84953                         if (d.tags.admin_level === '8' && d.tags.boundary === 'administrative')
84954                             { return true; }
84955                         if (d.tags.border_type === 'city')
84956                             { return true; }
84957                         if (d.tags.place === 'city' || d.tags.place === 'town' || d.tags.place === 'village')
84958                             { return true; }
84959                     }
84960
84961                     if (d.tags['addr:city'])
84962                         { return true; }
84963
84964                     return false;
84965                 }
84966             }
84967
84968             function getNearValues(key) {
84969                 var extent = combinedEntityExtent();
84970                 var l = extent.center();
84971                 var box = geoExtent(l).padByMeters(200);
84972
84973                 var results = context.history().intersects(box)
84974                     .filter(function hasTag(d) { return _entityIDs.indexOf(d.id) === -1 && d.tags[key]; })
84975                     .map(function(d) {
84976                         return {
84977                             title: d.tags[key],
84978                             value: d.tags[key],
84979                             dist: geoSphericalDistance(d.extent(context.graph()).center(), l)
84980                         };
84981                     })
84982                     .sort(function(a, b) {
84983                         return a.dist - b.dist;
84984                     });
84985
84986                 return utilArrayUniqBy(results, 'value');
84987             }
84988
84989
84990             function updateForCountryCode() {
84991
84992                 if (!_countryCode) { return; }
84993
84994                 var addressFormat;
84995                 for (var i = 0; i < _addressFormats.length; i++) {
84996                     var format = _addressFormats[i];
84997                     if (!format.countryCodes) {
84998                         addressFormat = format;   // choose the default format, keep going
84999                     } else if (format.countryCodes.indexOf(_countryCode) !== -1) {
85000                         addressFormat = format;   // choose the country format, stop here
85001                         break;
85002                     }
85003                 }
85004
85005                 var dropdowns = addressFormat.dropdowns || [
85006                     'city', 'county', 'country', 'district', 'hamlet',
85007                     'neighbourhood', 'place', 'postcode', 'province',
85008                     'quarter', 'state', 'street', 'subdistrict', 'suburb'
85009                 ];
85010
85011                 var widths = addressFormat.widths || {
85012                     housenumber: 1/3, street: 2/3,
85013                     city: 2/3, state: 1/4, postcode: 1/3
85014                 };
85015
85016                 function row(r) {
85017                     // Normalize widths.
85018                     var total = r.reduce(function(sum, key) {
85019                         return sum + (widths[key] || 0.5);
85020                     }, 0);
85021
85022                     return r.map(function(key) {
85023                         return {
85024                             id: key,
85025                             width: (widths[key] || 0.5) / total
85026                         };
85027                     });
85028                 }
85029
85030                 var rows = _wrap.selectAll('.addr-row')
85031                     .data(addressFormat.format, function(d) {
85032                         return d.toString();
85033                     });
85034
85035                 rows.exit()
85036                     .remove();
85037
85038                 rows
85039                     .enter()
85040                     .append('div')
85041                     .attr('class', 'addr-row')
85042                     .selectAll('input')
85043                     .data(row)
85044                     .enter()
85045                     .append('input')
85046                     .property('type', 'text')
85047                     .call(updatePlaceholder)
85048                     .attr('class', function (d) { return 'addr-' + d.id; })
85049                     .call(utilNoAuto)
85050                     .each(addDropdown)
85051                     .style('width', function (d) { return d.width * 100 + '%'; });
85052
85053
85054                 function addDropdown(d) {
85055                     if (dropdowns.indexOf(d.id) === -1) { return; }  // not a dropdown
85056
85057                     var nearValues = (d.id === 'street') ? getNearStreets
85058                         : (d.id === 'city') ? getNearCities
85059                         : getNearValues;
85060
85061                     select(this)
85062                         .call(uiCombobox(context, 'address-' + d.id)
85063                             .minItems(1)
85064                             .caseSensitive(true)
85065                             .fetcher(function(value, callback) {
85066                                 callback(nearValues('addr:' + d.id));
85067                             })
85068                         );
85069                 }
85070
85071                 _wrap.selectAll('input')
85072                     .on('blur', change())
85073                     .on('change', change());
85074
85075                 _wrap.selectAll('input:not(.combobox-input)')
85076                     .on('input', change(true));
85077
85078                 if (_tags) { updateTags(_tags); }
85079             }
85080
85081
85082             function address(selection) {
85083                 _selection = selection;
85084
85085                 _wrap = selection.selectAll('.form-field-input-wrap')
85086                     .data([0]);
85087
85088                 _wrap = _wrap.enter()
85089                     .append('div')
85090                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85091                     .merge(_wrap);
85092
85093                 var extent = combinedEntityExtent();
85094
85095                 if (extent) {
85096                     var countryCode;
85097                     if (context.inIntro()) {
85098                         // localize the address format for the walkthrough
85099                         countryCode = _t('intro.graph.countrycode');
85100                     } else {
85101                         var center = extent.center();
85102                         countryCode = iso1A2Code(center);
85103                     }
85104                     if (countryCode) {
85105                         _countryCode = countryCode.toLowerCase();
85106                         updateForCountryCode();
85107                     }
85108                 }
85109             }
85110
85111
85112             function change(onInput) {
85113                 return function() {
85114                     var tags = {};
85115
85116                     _wrap.selectAll('input')
85117                         .each(function (subfield) {
85118                             var key = field.key + ':' + subfield.id;
85119
85120                             var value = this.value;
85121                             if (!onInput) { value = context.cleanTagValue(value); }
85122
85123                             // don't override multiple values with blank string
85124                             if (Array.isArray(_tags[key]) && !value) { return; }
85125
85126                             tags[key] = value || undefined;
85127                         });
85128
85129                     dispatch$1.call('change', this, tags, onInput);
85130                 };
85131             }
85132
85133             function updatePlaceholder(inputSelection) {
85134                 return inputSelection.attr('placeholder', function(subfield) {
85135                     if (_tags && Array.isArray(_tags[field.key + ':' + subfield.id])) {
85136                         return _t('inspector.multiple_values');
85137                     }
85138                     if (_countryCode) {
85139                         var localkey = subfield.id + '!' + _countryCode;
85140                         var tkey = addrField.strings.placeholders[localkey] ? localkey : subfield.id;
85141                         return addrField.t('placeholders.' + tkey);
85142                     }
85143                 });
85144             }
85145
85146
85147             function updateTags(tags) {
85148                 utilGetSetValue(_wrap.selectAll('input'), function (subfield) {
85149                         var val = tags[field.key + ':' + subfield.id];
85150                         return typeof val === 'string' ? val : '';
85151                     })
85152                     .attr('title', function(subfield) {
85153                         var val = tags[field.key + ':' + subfield.id];
85154                         return val && Array.isArray(val) && val.filter(Boolean).join('\n');
85155                     })
85156                     .classed('mixed', function(subfield) {
85157                         return Array.isArray(tags[field.key + ':' + subfield.id]);
85158                     })
85159                     .call(updatePlaceholder);
85160             }
85161
85162
85163             function combinedEntityExtent() {
85164                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
85165             }
85166
85167
85168             address.entityIDs = function(val) {
85169                 if (!arguments.length) { return _entityIDs; }
85170                 _entityIDs = val;
85171                 return address;
85172             };
85173
85174
85175             address.tags = function(tags) {
85176                 _tags = tags;
85177                 updateTags(tags);
85178             };
85179
85180
85181             address.focus = function() {
85182                 var node = _wrap.selectAll('input').node();
85183                 if (node) { node.focus(); }
85184             };
85185
85186
85187             return utilRebind(address, dispatch$1, 'on');
85188         }
85189
85190         function uiFieldCycleway(field, context) {
85191             var dispatch$1 = dispatch('change');
85192             var items = select(null);
85193             var wrap = select(null);
85194             var _tags;
85195
85196             function cycleway(selection) {
85197
85198                 function stripcolon(s) {
85199                     return s.replace(':', '');
85200                 }
85201
85202
85203                 wrap = selection.selectAll('.form-field-input-wrap')
85204                     .data([0]);
85205
85206                 wrap = wrap.enter()
85207                     .append('div')
85208                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85209                     .merge(wrap);
85210
85211
85212                 var div = wrap.selectAll('ul')
85213                     .data([0]);
85214
85215                 div = div.enter()
85216                     .append('ul')
85217                     .attr('class', 'rows')
85218                     .merge(div);
85219
85220                 var keys = ['cycleway:left', 'cycleway:right'];
85221
85222                 items = div.selectAll('li')
85223                     .data(keys);
85224
85225                 var enter = items.enter()
85226                     .append('li')
85227                     .attr('class', function(d) { return 'labeled-input preset-cycleway-' + stripcolon(d); });
85228
85229                 enter
85230                     .append('span')
85231                     .attr('class', 'label preset-label-cycleway')
85232                     .attr('for', function(d) { return 'preset-input-cycleway-' + stripcolon(d); })
85233                     .text(function(d) { return field.t('types.' + d); });
85234
85235                 enter
85236                     .append('div')
85237                     .attr('class', 'preset-input-cycleway-wrap')
85238                     .append('input')
85239                     .attr('type', 'text')
85240                     .attr('class', function(d) { return 'preset-input-cycleway preset-input-' + stripcolon(d); })
85241                     .call(utilNoAuto)
85242                     .each(function(d) {
85243                         select(this)
85244                             .call(uiCombobox(context, 'cycleway-' + stripcolon(d))
85245                                 .data(cycleway.options(d))
85246                             );
85247                     });
85248
85249                 items = items.merge(enter);
85250
85251                 // Update
85252                 wrap.selectAll('.preset-input-cycleway')
85253                     .on('change', change)
85254                     .on('blur', change);
85255             }
85256
85257
85258             function change(key) {
85259
85260                 var newValue = context.cleanTagValue(utilGetSetValue(select(this)));
85261
85262                 // don't override multiple values with blank string
85263                 if (!newValue && (Array.isArray(_tags.cycleway) || Array.isArray(_tags[key]))) { return; }
85264
85265                 if (newValue === 'none' || newValue === '') { newValue = undefined; }
85266
85267                 var otherKey = key === 'cycleway:left' ? 'cycleway:right' : 'cycleway:left';
85268                 var otherValue = typeof _tags.cycleway === 'string' ? _tags.cycleway : _tags[otherKey];
85269                 if (otherValue && Array.isArray(otherValue)) {
85270                     // we must always have an explicit value for comparison
85271                     otherValue = otherValue[0];
85272                 }
85273                 if (otherValue === 'none' || otherValue === '') { otherValue = undefined; }
85274
85275                 var tag = {};
85276
85277                 // If the left and right tags match, use the cycleway tag to tag both
85278                 // sides the same way
85279                 if (newValue === otherValue) {
85280                     tag = {
85281                         cycleway: newValue,
85282                         'cycleway:left': undefined,
85283                         'cycleway:right': undefined
85284                     };
85285                 } else {
85286                     // Always set both left and right as changing one can affect the other
85287                     tag = {
85288                         cycleway: undefined
85289                     };
85290                     tag[key] = newValue;
85291                     tag[otherKey] = otherValue;
85292                 }
85293
85294                 dispatch$1.call('change', this, tag);
85295             }
85296
85297
85298             cycleway.options = function() {
85299                 return Object.keys(field.strings.options).map(function(option) {
85300                     return {
85301                         title: field.t('options.' + option + '.description'),
85302                         value: option
85303                     };
85304                 });
85305             };
85306
85307
85308             cycleway.tags = function(tags) {
85309                 _tags = tags;
85310
85311                 // If cycleway is set, use that instead of individual values
85312                 var commonValue = typeof tags.cycleway === 'string' && tags.cycleway;
85313
85314                 utilGetSetValue(items.selectAll('.preset-input-cycleway'), function(d) {
85315                         if (commonValue) { return commonValue; }
85316                         return !tags.cycleway && typeof tags[d] === 'string' ? tags[d] : '';
85317                     })
85318                     .attr('title', function(d) {
85319                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85320                             var vals = [];
85321                             if (Array.isArray(tags.cycleway)) {
85322                                 vals = vals.concat(tags.cycleway);
85323                             }
85324                             if (Array.isArray(tags[d])) {
85325                                 vals = vals.concat(tags[d]);
85326                             }
85327                             return vals.filter(Boolean).join('\n');
85328                         }
85329                         return null;
85330                     })
85331                     .attr('placeholder', function(d) {
85332                         if (Array.isArray(tags.cycleway) || Array.isArray(tags[d])) {
85333                             return _t('inspector.multiple_values');
85334                         }
85335                         return field.placeholder();
85336                     })
85337                     .classed('mixed', function(d) {
85338                         return Array.isArray(tags.cycleway) || Array.isArray(tags[d]);
85339                     });
85340             };
85341
85342
85343             cycleway.focus = function() {
85344                 var node = wrap.selectAll('input').node();
85345                 if (node) { node.focus(); }
85346             };
85347
85348
85349             return utilRebind(cycleway, dispatch$1, 'on');
85350         }
85351
85352         function uiFieldLanes(field, context) {
85353             var dispatch$1 = dispatch('change');
85354             var LANE_WIDTH = 40;
85355             var LANE_HEIGHT = 200;
85356             var _entityIDs = [];
85357
85358             function lanes(selection) {
85359                 var lanesData = context.entity(_entityIDs[0]).lanes();
85360
85361                 if (!context.container().select('.inspector-wrap.inspector-hidden').empty() || !selection.node().parentNode) {
85362                     selection.call(lanes.off);
85363                     return;
85364                 }
85365
85366                 var wrap = selection.selectAll('.form-field-input-wrap')
85367                     .data([0]);
85368
85369                 wrap = wrap.enter()
85370                     .append('div')
85371                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85372                     .merge(wrap);
85373
85374                 var surface =  wrap.selectAll('.surface')
85375                     .data([0]);
85376
85377                 var d = utilGetDimensions(wrap);
85378                 var freeSpace = d[0] - lanesData.lanes.length * LANE_WIDTH * 1.5 + LANE_WIDTH * 0.5;
85379
85380                 surface = surface.enter()
85381                     .append('svg')
85382                     .attr('width', d[0])
85383                     .attr('height', 300)
85384                     .attr('class', 'surface')
85385                     .merge(surface);
85386
85387
85388                 var lanesSelection = surface.selectAll('.lanes')
85389                     .data([0]);
85390
85391                 lanesSelection = lanesSelection.enter()
85392                     .append('g')
85393                     .attr('class', 'lanes')
85394                     .merge(lanesSelection);
85395
85396                 lanesSelection
85397                     .attr('transform', function () {
85398                         return 'translate(' + (freeSpace / 2) + ', 0)';
85399                     });
85400
85401
85402                 var lane = lanesSelection.selectAll('.lane')
85403                    .data(lanesData.lanes);
85404
85405                 lane.exit()
85406                     .remove();
85407
85408                 var enter = lane.enter()
85409                     .append('g')
85410                     .attr('class', 'lane');
85411
85412                 enter
85413                     .append('g')
85414                     .append('rect')
85415                     .attr('y', 50)
85416                     .attr('width', LANE_WIDTH)
85417                     .attr('height', LANE_HEIGHT);
85418
85419                 enter
85420                     .append('g')
85421                     .attr('class', 'forward')
85422                     .append('text')
85423                     .attr('y', 40)
85424                     .attr('x', 14)
85425                     .text('▲');
85426
85427                 enter
85428                     .append('g')
85429                     .attr('class', 'bothways')
85430                     .append('text')
85431                     .attr('y', 40)
85432                     .attr('x', 14)
85433                     .text('▲▼');
85434
85435                 enter
85436                     .append('g')
85437                     .attr('class', 'backward')
85438                     .append('text')
85439                     .attr('y', 40)
85440                     .attr('x', 14)
85441                     .text('▼');
85442
85443
85444                 lane = lane
85445                     .merge(enter);
85446
85447                 lane
85448                     .attr('transform', function(d) {
85449                         return 'translate(' + (LANE_WIDTH * d.index * 1.5) + ', 0)';
85450                     });
85451
85452                 lane.select('.forward')
85453                     .style('visibility', function(d) {
85454                         return d.direction === 'forward' ? 'visible' : 'hidden';
85455                     });
85456
85457                 lane.select('.bothways')
85458                     .style('visibility', function(d) {
85459                         return d.direction === 'bothways' ? 'visible' : 'hidden';
85460                     });
85461
85462                 lane.select('.backward')
85463                     .style('visibility', function(d) {
85464                         return d.direction === 'backward' ? 'visible' : 'hidden';
85465                     });
85466             }
85467
85468
85469             lanes.entityIDs = function(val) {
85470                 _entityIDs = val;
85471             };
85472
85473             lanes.tags = function() {};
85474             lanes.focus = function() {};
85475             lanes.off = function() {};
85476
85477             return utilRebind(lanes, dispatch$1, 'on');
85478         }
85479
85480         uiFieldLanes.supportsMultiselection = false;
85481
85482         var _languagesArray = [];
85483
85484
85485         function uiFieldLocalized(field, context) {
85486             var dispatch$1 = dispatch('change', 'input');
85487             var wikipedia = services.wikipedia;
85488             var input = select(null);
85489             var localizedInputs = select(null);
85490             var _countryCode;
85491             var _tags;
85492
85493
85494             // A concern here in switching to async data means that _languagesArray will not
85495             // be available the first time through, so things like the fetchers and
85496             // the language() function will not work immediately.
85497             _mainFileFetcher.get('languages')
85498                 .then(loadLanguagesArray)
85499                 .catch(function() { /* ignore */ });
85500
85501             var _territoryLanguages = {};
85502             _mainFileFetcher.get('territory_languages')
85503                 .then(function(d) { _territoryLanguages = d; })
85504                 .catch(function() { /* ignore */ });
85505
85506
85507             var allSuggestions = _mainPresetIndex.collection.filter(function(p) {
85508                 return p.suggestion === true;
85509             });
85510
85511             // reuse these combos
85512             var langCombo = uiCombobox(context, 'localized-lang')
85513                 .fetcher(fetchLanguages)
85514                 .minItems(0);
85515
85516             var brandCombo = uiCombobox(context, 'localized-brand')
85517                 .canAutocomplete(false)
85518                 .minItems(1);
85519
85520             var _selection = select(null);
85521             var _multilingual = [];
85522             var _buttonTip = uiTooltip()
85523                 .title(_t('translate.translate'))
85524                 .placement('left');
85525             var _wikiTitles;
85526             var _entityIDs = [];
85527
85528
85529             function loadLanguagesArray(dataLanguages) {
85530                 if (_languagesArray.length !== 0) { return; }
85531
85532                 // some conversion is needed to ensure correct OSM tags are used
85533                 var replacements = {
85534                     sr: 'sr-Cyrl',      // in OSM, `sr` implies Cyrillic
85535                     'sr-Cyrl': false    // `sr-Cyrl` isn't used in OSM
85536                 };
85537
85538                 for (var code in dataLanguages) {
85539                     if (replacements[code] === false) { continue; }
85540                     var metaCode = code;
85541                     if (replacements[code]) { metaCode = replacements[code]; }
85542
85543                     _languagesArray.push({
85544                         localName: _mainLocalizer.languageName(metaCode, { localOnly: true }),
85545                         nativeName: dataLanguages[metaCode].nativeName,
85546                         code: code,
85547                         label: _mainLocalizer.languageName(metaCode)
85548                     });
85549                 }
85550             }
85551
85552
85553             function calcLocked() {
85554
85555                 // only lock the Name field
85556                 var isLocked = field.id === 'name' &&
85557                     _entityIDs.length &&
85558                     // lock the field if any feature needs it
85559                     _entityIDs.some(function(entityID) {
85560
85561                         var entity = context.graph().hasEntity(entityID);
85562                         if (!entity) { return false; }
85563
85564                         var original = context.graph().base().entities[_entityIDs[0]];
85565                         var hasOriginalName = original && entity.tags.name && entity.tags.name === original.tags.name;
85566                         // if the name was already edited manually then allow further editing
85567                         if (!hasOriginalName) { return false; }
85568
85569                         // features linked to Wikidata are likely important and should be protected
85570                         if (entity.tags.wikidata) { return true; }
85571
85572                         // assume the name has already been confirmed if its source has been researched
85573                         if (entity.tags['name:etymology:wikidata']) { return true; }
85574
85575                         var preset = _mainPresetIndex.match(entity, context.graph());
85576                         var isSuggestion = preset && preset.suggestion;
85577                         var showsBrand = preset && preset.originalFields.filter(function(d) {
85578                             return d.id === 'brand';
85579                         }).length;
85580                         // protect standardized brand names
85581                         return isSuggestion && !showsBrand;
85582                     });
85583
85584                 field.locked(isLocked);
85585             }
85586
85587
85588             // update _multilingual, maintaining the existing order
85589             function calcMultilingual(tags) {
85590                 var existingLangsOrdered = _multilingual.map(function(item) {
85591                     return item.lang;
85592                 });
85593                 var existingLangs = new Set(existingLangsOrdered.filter(Boolean));
85594
85595                 for (var k in tags) {
85596                     var m = k.match(/^(.*):([a-zA-Z_-]+)$/);
85597                     if (m && m[1] === field.key && m[2]) {
85598                         var item = { lang: m[2], value: tags[k] };
85599                         if (existingLangs.has(item.lang)) {
85600                             // update the value
85601                             _multilingual[existingLangsOrdered.indexOf(item.lang)].value = item.value;
85602                             existingLangs.delete(item.lang);
85603                         } else {
85604                             _multilingual.push(item);
85605                         }
85606                     }
85607                 }
85608
85609                 _multilingual = _multilingual.filter(function(item) {
85610                     return !item.lang || !existingLangs.has(item.lang);
85611                 });
85612             }
85613
85614
85615             function localized(selection) {
85616                 _selection = selection;
85617                 calcLocked();
85618                 var isLocked = field.locked();
85619                 var singularEntity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85620                 var preset = singularEntity && _mainPresetIndex.match(singularEntity, context.graph());
85621
85622                 var wrap = selection.selectAll('.form-field-input-wrap')
85623                     .data([0]);
85624
85625                 // enter/update
85626                 wrap = wrap.enter()
85627                     .append('div')
85628                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
85629                     .merge(wrap);
85630
85631                 input = wrap.selectAll('.localized-main')
85632                     .data([0]);
85633
85634                 // enter/update
85635                 input = input.enter()
85636                     .append('input')
85637                     .attr('type', 'text')
85638                     .attr('id', field.domId)
85639                     .attr('class', 'localized-main')
85640                     .call(utilNoAuto)
85641                     .merge(input);
85642
85643                 if (preset && field.id === 'name') {
85644                     var pTag = preset.id.split('/', 2);
85645                     var pKey = pTag[0];
85646                     var pValue = pTag[1];
85647
85648                     if (!preset.suggestion) {
85649                         // Not a suggestion preset - Add a suggestions dropdown if it makes sense to.
85650                         // This code attempts to determine if the matched preset is the
85651                         // kind of preset that even can benefit from name suggestions..
85652                         // - true = shops, cafes, hotels, etc. (also generic and fallback presets)
85653                         // - false = churches, parks, hospitals, etc. (things not in the index)
85654                         var isFallback = preset.isFallback();
85655                         var goodSuggestions = allSuggestions.filter(function(s) {
85656                             if (isFallback) { return true; }
85657                             var sTag = s.id.split('/', 2);
85658                             var sKey = sTag[0];
85659                             var sValue = sTag[1];
85660                             return pKey === sKey && (!pValue || pValue === sValue);
85661                         });
85662
85663                         // Show the suggestions.. If the user picks one, change the tags..
85664                         if (allSuggestions.length && goodSuggestions.length) {
85665                             input
85666                                 .on('blur.localized', checkBrandOnBlur)
85667                                 .call(brandCombo
85668                                     .fetcher(fetchBrandNames(preset, allSuggestions))
85669                                     .on('accept', acceptBrand)
85670                                     .on('cancel', cancelBrand)
85671                                 );
85672                         }
85673                     }
85674                 }
85675
85676                 input
85677                     .classed('disabled', !!isLocked)
85678                     .attr('readonly', isLocked || null)
85679                     .on('input', change(true))
85680                     .on('blur', change())
85681                     .on('change', change());
85682
85683
85684                 var translateButton = wrap.selectAll('.localized-add')
85685                     .data([0]);
85686
85687                 translateButton = translateButton.enter()
85688                     .append('button')
85689                     .attr('class', 'localized-add form-field-button')
85690                     .attr('tabindex', -1)
85691                     .call(svgIcon('#iD-icon-plus'))
85692                     .merge(translateButton);
85693
85694                 translateButton
85695                     .classed('disabled', !!isLocked)
85696                     .call(isLocked ? _buttonTip.destroy : _buttonTip)
85697                     .on('click', addNew);
85698
85699
85700                 if (_tags && !_multilingual.length) {
85701                     calcMultilingual(_tags);
85702                 }
85703
85704                 localizedInputs = selection.selectAll('.localized-multilingual')
85705                     .data([0]);
85706
85707                 localizedInputs = localizedInputs.enter()
85708                     .append('div')
85709                     .attr('class', 'localized-multilingual')
85710                     .merge(localizedInputs);
85711
85712                 localizedInputs
85713                     .call(renderMultilingual);
85714
85715                 localizedInputs.selectAll('button, input')
85716                     .classed('disabled', !!isLocked)
85717                     .attr('readonly', isLocked || null);
85718
85719
85720
85721                 // We are not guaranteed to get an `accept` or `cancel` when blurring the field.
85722                 // (This can happen if the user actives the combo, arrows down, and then clicks off to blur)
85723                 // So compare the current field value against the suggestions one last time.
85724                 function checkBrandOnBlur() {
85725                     var latest = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85726                     if (!latest) { return; }   // deleting the entity blurred the field?
85727
85728                     var preset = _mainPresetIndex.match(latest, context.graph());
85729                     if (preset && preset.suggestion) { return; }   // already accepted
85730
85731                     // note: here we are testing against "decorated" names, i.e. 'Starbucks – Cafe'
85732                     var name = utilGetSetValue(input).trim();
85733                     var matched = allSuggestions.filter(function(s) { return name === s.name(); });
85734
85735                     if (matched.length === 1) {
85736                         acceptBrand({ suggestion: matched[0] });
85737                     } else {
85738                         cancelBrand();
85739                     }
85740                 }
85741
85742
85743                 function acceptBrand(d) {
85744
85745                     var entity = _entityIDs.length === 1 && context.hasEntity(_entityIDs[0]);
85746
85747                     if (!d || !entity) {
85748                         cancelBrand();
85749                         return;
85750                     }
85751
85752                     var tags = entity.tags;
85753                     var geometry = entity.geometry(context.graph());
85754                     var removed = preset.unsetTags(tags, geometry);
85755                     for (var k in tags) {
85756                         tags[k] = removed[k];  // set removed tags to `undefined`
85757                     }
85758                     tags = d.suggestion.setTags(tags, geometry);
85759                     utilGetSetValue(input, tags.name);
85760                     dispatch$1.call('change', this, tags);
85761                 }
85762
85763
85764                 // user hit escape, clean whatever preset name appears after the last ' – '
85765                 function cancelBrand() {
85766                     var name = utilGetSetValue(input);
85767                     var clean = cleanName(name);
85768                     if (clean !== name) {
85769                         utilGetSetValue(input, clean);
85770                         dispatch$1.call('change', this, { name: clean });
85771                     }
85772                 }
85773
85774                 // Remove whatever is after the last ' – '
85775                 // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
85776                 function cleanName(name) {
85777                     var parts = name.split(' – ');
85778                     if (parts.length > 1) {
85779                         parts.pop();
85780                         name = parts.join(' – ');
85781                     }
85782                     return name;
85783                 }
85784
85785
85786                 function fetchBrandNames(preset, suggestions) {
85787                     var pTag = preset.id.split('/', 2);
85788                     var pKey = pTag[0];
85789                     var pValue = pTag[1];
85790
85791                     return function(value, callback) {
85792                         var results = [];
85793                         if (value && value.length > 2) {
85794                             for (var i = 0; i < suggestions.length; i++) {
85795                                 var s = suggestions[i];
85796
85797                                 // don't suggest brands from incompatible countries
85798                                 if (_countryCode && s.countryCodes &&
85799                                     s.countryCodes.indexOf(_countryCode) === -1) { continue; }
85800
85801                                 var sTag = s.id.split('/', 2);
85802                                 var sKey = sTag[0];
85803                                 var sValue = sTag[1];
85804                                 var name = s.name();
85805                                 var dist = utilEditDistance(value, name.substring(0, value.length));
85806                                 var matchesPreset = (pKey === sKey && (!pValue || pValue === sValue));
85807
85808                                 if (dist < 1 || (matchesPreset && dist < 3)) {
85809                                     var obj = {
85810                                         title: name,
85811                                         value: name,
85812                                         suggestion: s,
85813                                         dist: dist + (matchesPreset ? 0 : 1)  // penalize if not matched preset
85814                                     };
85815                                     results.push(obj);
85816                                 }
85817                             }
85818                             results.sort(function(a, b) { return a.dist - b.dist; });
85819                         }
85820                         results = results.slice(0, 10);
85821                         callback(results);
85822                     };
85823                 }
85824
85825
85826                 function addNew() {
85827                     event.preventDefault();
85828                     if (field.locked()) { return; }
85829
85830                     var defaultLang = _mainLocalizer.languageCode().toLowerCase();
85831                     var langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85832                     var isLangEn = defaultLang.indexOf('en') > -1;
85833                     if (isLangEn || langExists) {
85834                         defaultLang = '';
85835                         langExists = _multilingual.find(function(datum) { return datum.lang === defaultLang; });
85836                     }
85837
85838                     if (!langExists) {
85839                         // prepend the value so it appears at the top
85840                         _multilingual.unshift({ lang: defaultLang, value: '' });
85841
85842                         localizedInputs
85843                             .call(renderMultilingual);
85844                     }
85845                 }
85846
85847
85848                 function change(onInput) {
85849                     return function() {
85850                         if (field.locked()) {
85851                             event.preventDefault();
85852                             return;
85853                         }
85854
85855                         var val = utilGetSetValue(select(this));
85856                         if (!onInput) { val = context.cleanTagValue(val); }
85857
85858                         // don't override multiple values with blank string
85859                         if (!val && Array.isArray(_tags[field.key])) { return; }
85860
85861                         var t = {};
85862
85863                         t[field.key] = val || undefined;
85864                         dispatch$1.call('change', this, t, onInput);
85865                     };
85866                 }
85867             }
85868
85869
85870             function key(lang) {
85871                 return field.key + ':' + lang;
85872             }
85873
85874
85875             function changeLang(d) {
85876                 var tags = {};
85877
85878                 // make sure unrecognized suffixes are lowercase - #7156
85879                 var lang = utilGetSetValue(select(this)).toLowerCase();
85880
85881                 var language = _languagesArray.find(function(d) {
85882                     return d.label.toLowerCase() === lang ||
85883                         (d.localName && d.localName.toLowerCase() === lang) ||
85884                         (d.nativeName && d.nativeName.toLowerCase() === lang);
85885                 });
85886                 if (language) { lang = language.code; }
85887
85888                 if (d.lang && d.lang !== lang) {
85889                     tags[key(d.lang)] = undefined;
85890                 }
85891
85892                 var newKey = lang && context.cleanTagKey(key(lang));
85893
85894                 var value = utilGetSetValue(select(this.parentNode).selectAll('.localized-value'));
85895
85896                 if (newKey && value) {
85897                     tags[newKey] = value;
85898                 } else if (newKey && _wikiTitles && _wikiTitles[d.lang]) {
85899                     tags[newKey] = _wikiTitles[d.lang];
85900                 }
85901
85902                 d.lang = lang;
85903                 dispatch$1.call('change', this, tags);
85904             }
85905
85906
85907             function changeValue(d) {
85908                 if (!d.lang) { return; }
85909                 var value = context.cleanTagValue(utilGetSetValue(select(this))) || undefined;
85910
85911                 // don't override multiple values with blank string
85912                 if (!value && Array.isArray(d.value)) { return; }
85913
85914                 var t = {};
85915                 t[key(d.lang)] = value;
85916                 d.value = value;
85917                 dispatch$1.call('change', this, t);
85918             }
85919
85920
85921             function fetchLanguages(value, cb) {
85922                 var v = value.toLowerCase();
85923
85924                 // show the user's language first
85925                 var langCodes = [_mainLocalizer.localeCode(), _mainLocalizer.languageCode()];
85926
85927                 if (_countryCode && _territoryLanguages[_countryCode]) {
85928                     langCodes = langCodes.concat(_territoryLanguages[_countryCode]);
85929                 }
85930
85931                 var langItems = [];
85932                 langCodes.forEach(function(code) {
85933                     var langItem = _languagesArray.find(function(item) {
85934                         return item.code === code;
85935                     });
85936                     if (langItem) { langItems.push(langItem); }
85937                 });
85938                 langItems = utilArrayUniq(langItems.concat(_languagesArray));
85939
85940                 cb(langItems.filter(function(d) {
85941                     return d.label.toLowerCase().indexOf(v) >= 0 ||
85942                         (d.localName && d.localName.toLowerCase().indexOf(v) >= 0) ||
85943                         (d.nativeName && d.nativeName.toLowerCase().indexOf(v) >= 0) ||
85944                         d.code.toLowerCase().indexOf(v) >= 0;
85945                 }).map(function(d) {
85946                     return { value: d.label };
85947                 }));
85948             }
85949
85950
85951             function renderMultilingual(selection) {
85952                 var entries = selection.selectAll('div.entry')
85953                     .data(_multilingual, function(d) { return d.lang; });
85954
85955                 entries.exit()
85956                     .style('top', '0')
85957                     .style('max-height', '240px')
85958                     .transition()
85959                     .duration(200)
85960                     .style('opacity', '0')
85961                     .style('max-height', '0px')
85962                     .remove();
85963
85964                 var entriesEnter = entries.enter()
85965                     .append('div')
85966                     .attr('class', 'entry')
85967                     .each(function(_, index) {
85968                         var wrap = select(this);
85969
85970                         var domId = utilUniqueDomId(index);
85971
85972                         var label = wrap
85973                             .append('label')
85974                             .attr('class', 'field-label')
85975                             .attr('for', domId);
85976
85977                         var text = label
85978                             .append('span')
85979                             .attr('class', 'label-text');
85980
85981                         text
85982                             .append('span')
85983                             .attr('class', 'label-textvalue')
85984                             .text(_t('translate.localized_translation_label'));
85985
85986                         text
85987                             .append('span')
85988                             .attr('class', 'label-textannotation');
85989
85990                         label
85991                             .append('button')
85992                             .attr('class', 'remove-icon-multilingual')
85993                             .on('click', function(d, index) {
85994                                 if (field.locked()) { return; }
85995                                 event.preventDefault();
85996
85997                                 if (!d.lang || !d.value) {
85998                                     _multilingual.splice(index, 1);
85999                                     renderMultilingual(selection);
86000                                 } else {
86001                                     // remove from entity tags
86002                                     var t = {};
86003                                     t[key(d.lang)] = undefined;
86004                                     dispatch$1.call('change', this, t);
86005                                 }
86006
86007                             })
86008                             .call(svgIcon('#iD-operation-delete'));
86009
86010                         wrap
86011                             .append('input')
86012                             .attr('class', 'localized-lang')
86013                             .attr('id', domId)
86014                             .attr('type', 'text')
86015                             .attr('placeholder', _t('translate.localized_translation_language'))
86016                             .on('blur', changeLang)
86017                             .on('change', changeLang)
86018                             .call(langCombo);
86019
86020                         wrap
86021                             .append('input')
86022                             .attr('type', 'text')
86023                             .attr('class', 'localized-value')
86024                             .on('blur', changeValue)
86025                             .on('change', changeValue);
86026                     });
86027
86028                 entriesEnter
86029                     .style('margin-top', '0px')
86030                     .style('max-height', '0px')
86031                     .style('opacity', '0')
86032                     .transition()
86033                     .duration(200)
86034                     .style('margin-top', '10px')
86035                     .style('max-height', '240px')
86036                     .style('opacity', '1')
86037                     .on('end', function() {
86038                         select(this)
86039                             .style('max-height', '')
86040                             .style('overflow', 'visible');
86041                     });
86042
86043                 entries = entries.merge(entriesEnter);
86044
86045                 entries.order();
86046
86047                 entries.classed('present', function(d) {
86048                     return d.lang && d.value;
86049                 });
86050
86051                 utilGetSetValue(entries.select('.localized-lang'), function(d) {
86052                     return _mainLocalizer.languageName(d.lang);
86053                 });
86054
86055                 utilGetSetValue(entries.select('.localized-value'), function(d) {
86056                         return typeof d.value === 'string' ? d.value : '';
86057                     })
86058                     .attr('title', function(d) {
86059                         return Array.isArray(d.value) ? d.value.filter(Boolean).join('\n') : null;
86060                     })
86061                     .attr('placeholder', function(d) {
86062                         return Array.isArray(d.value) ? _t('inspector.multiple_values') : _t('translate.localized_translation_name');
86063                     })
86064                     .classed('mixed', function(d) {
86065                         return Array.isArray(d.value);
86066                     });
86067             }
86068
86069
86070             localized.tags = function(tags) {
86071                 _tags = tags;
86072
86073                 // Fetch translations from wikipedia
86074                 if (typeof tags.wikipedia === 'string' && !_wikiTitles) {
86075                     _wikiTitles = {};
86076                     var wm = tags.wikipedia.match(/([^:]+):(.+)/);
86077                     if (wm && wm[0] && wm[1]) {
86078                         wikipedia.translations(wm[1], wm[2], function(err, d) {
86079                             if (err || !d) { return; }
86080                             _wikiTitles = d;
86081                         });
86082                     }
86083                 }
86084
86085                 var isMixed = Array.isArray(tags[field.key]);
86086
86087                 utilGetSetValue(input, typeof tags[field.key] === 'string' ? tags[field.key] : '')
86088                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
86089                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86090                     .classed('mixed', isMixed);
86091
86092                 calcMultilingual(tags);
86093
86094                 _selection
86095                     .call(localized);
86096             };
86097
86098
86099             localized.focus = function() {
86100                 input.node().focus();
86101             };
86102
86103
86104             localized.entityIDs = function(val) {
86105                 if (!arguments.length) { return _entityIDs; }
86106                 _entityIDs = val;
86107                 _multilingual = [];
86108                 loadCountryCode();
86109                 return localized;
86110             };
86111
86112             function loadCountryCode() {
86113                 var extent = combinedEntityExtent();
86114                 var countryCode = extent && iso1A2Code(extent.center());
86115                 _countryCode = countryCode && countryCode.toLowerCase();
86116             }
86117
86118             function combinedEntityExtent() {
86119                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86120             }
86121
86122             return utilRebind(localized, dispatch$1, 'on');
86123         }
86124
86125         function uiFieldMaxspeed(field, context) {
86126             var dispatch$1 = dispatch('change');
86127             var unitInput = select(null);
86128             var input = select(null);
86129             var _entityIDs = [];
86130             var _tags;
86131             var _isImperial;
86132
86133             var speedCombo = uiCombobox(context, 'maxspeed');
86134             var unitCombo = uiCombobox(context, 'maxspeed-unit')
86135                     .data(['km/h', 'mph'].map(comboValues));
86136
86137             var metricValues = [20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120];
86138             var imperialValues = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80];
86139
86140
86141             function maxspeed(selection) {
86142
86143                 var wrap = selection.selectAll('.form-field-input-wrap')
86144                     .data([0]);
86145
86146                 wrap = wrap.enter()
86147                     .append('div')
86148                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86149                     .merge(wrap);
86150
86151
86152                 input = wrap.selectAll('input.maxspeed-number')
86153                     .data([0]);
86154
86155                 input = input.enter()
86156                     .append('input')
86157                     .attr('type', 'text')
86158                     .attr('class', 'maxspeed-number')
86159                     .attr('id', field.domId)
86160                     .call(utilNoAuto)
86161                     .call(speedCombo)
86162                     .merge(input);
86163
86164                 input
86165                     .on('change', change)
86166                     .on('blur', change);
86167
86168                 var loc = combinedEntityExtent().center();
86169                 _isImperial = roadSpeedUnit(loc) === 'mph';
86170
86171                 unitInput = wrap.selectAll('input.maxspeed-unit')
86172                     .data([0]);
86173
86174                 unitInput = unitInput.enter()
86175                     .append('input')
86176                     .attr('type', 'text')
86177                     .attr('class', 'maxspeed-unit')
86178                     .call(unitCombo)
86179                     .merge(unitInput);
86180
86181                 unitInput
86182                     .on('blur', changeUnits)
86183                     .on('change', changeUnits);
86184
86185
86186                 function changeUnits() {
86187                     _isImperial = utilGetSetValue(unitInput) === 'mph';
86188                     utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86189                     setUnitSuggestions();
86190                     change();
86191                 }
86192             }
86193
86194
86195             function setUnitSuggestions() {
86196                 speedCombo.data((_isImperial ? imperialValues : metricValues).map(comboValues));
86197                 utilGetSetValue(unitInput, _isImperial ? 'mph' : 'km/h');
86198             }
86199
86200
86201             function comboValues(d) {
86202                 return {
86203                     value: d.toString(),
86204                     title: d.toString()
86205                 };
86206             }
86207
86208
86209             function change() {
86210                 var tag = {};
86211                 var value = utilGetSetValue(input).trim();
86212
86213                 // don't override multiple values with blank string
86214                 if (!value && Array.isArray(_tags[field.key])) { return; }
86215
86216                 if (!value) {
86217                     tag[field.key] = undefined;
86218                 } else if (isNaN(value) || !_isImperial) {
86219                     tag[field.key] = context.cleanTagValue(value);
86220                 } else {
86221                     tag[field.key] = context.cleanTagValue(value + ' mph');
86222                 }
86223
86224                 dispatch$1.call('change', this, tag);
86225             }
86226
86227
86228             maxspeed.tags = function(tags) {
86229                 _tags = tags;
86230
86231                 var value = tags[field.key];
86232                 var isMixed = Array.isArray(value);
86233
86234                 if (!isMixed) {
86235                     if (value && value.indexOf('mph') >= 0) {
86236                         value = parseInt(value, 10).toString();
86237                         _isImperial = true;
86238                     } else if (value) {
86239                         _isImperial = false;
86240                     }
86241                 }
86242
86243                 setUnitSuggestions();
86244
86245                 utilGetSetValue(input, typeof value === 'string' ? value : '')
86246                     .attr('title', isMixed ? value.filter(Boolean).join('\n') : null)
86247                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : field.placeholder())
86248                     .classed('mixed', isMixed);
86249             };
86250
86251
86252             maxspeed.focus = function() {
86253                 input.node().focus();
86254             };
86255
86256
86257             maxspeed.entityIDs = function(val) {
86258                 _entityIDs = val;
86259             };
86260
86261
86262             function combinedEntityExtent() {
86263                 return _entityIDs && _entityIDs.length && utilTotalExtent(_entityIDs, context.graph());
86264             }
86265
86266
86267             return utilRebind(maxspeed, dispatch$1, 'on');
86268         }
86269
86270         function uiFieldRadio(field, context) {
86271             var dispatch$1 = dispatch('change');
86272             var placeholder = select(null);
86273             var wrap = select(null);
86274             var labels = select(null);
86275             var radios = select(null);
86276             var radioData = (field.options || (field.strings && field.strings.options && Object.keys(field.strings.options)) || field.keys).slice();  // shallow copy
86277             var typeField;
86278             var layerField;
86279             var _oldType = {};
86280             var _entityIDs = [];
86281
86282
86283             function selectedKey() {
86284                 var node = wrap.selectAll('.form-field-input-radio label.active input');
86285                 return !node.empty() && node.datum();
86286             }
86287
86288
86289             function radio(selection) {
86290                 selection.classed('preset-radio', true);
86291
86292                 wrap = selection.selectAll('.form-field-input-wrap')
86293                     .data([0]);
86294
86295                 var enter = wrap.enter()
86296                     .append('div')
86297                     .attr('class', 'form-field-input-wrap form-field-input-radio');
86298
86299                 enter
86300                     .append('span')
86301                     .attr('class', 'placeholder');
86302
86303                 wrap = wrap
86304                     .merge(enter);
86305
86306
86307                 placeholder = wrap.selectAll('.placeholder');
86308
86309                 labels = wrap.selectAll('label')
86310                     .data(radioData);
86311
86312                 enter = labels.enter()
86313                     .append('label');
86314
86315                 enter
86316                     .append('input')
86317                     .attr('type', 'radio')
86318                     .attr('name', field.id)
86319                     .attr('value', function(d) { return field.t('options.' + d, { 'default': d }); })
86320                     .attr('checked', false);
86321
86322                 enter
86323                     .append('span')
86324                     .text(function(d) { return field.t('options.' + d, { 'default': d }); });
86325
86326                 labels = labels
86327                     .merge(enter);
86328
86329                 radios = labels.selectAll('input')
86330                     .on('change', changeRadio);
86331
86332             }
86333
86334
86335             function structureExtras(selection, tags) {
86336                 var selected = selectedKey() || tags.layer !== undefined;
86337                 var type = _mainPresetIndex.field(selected);
86338                 var layer = _mainPresetIndex.field('layer');
86339                 var showLayer = (selected === 'bridge' || selected === 'tunnel' || tags.layer !== undefined);
86340
86341
86342                 var extrasWrap = selection.selectAll('.structure-extras-wrap')
86343                     .data(selected ? [0] : []);
86344
86345                 extrasWrap.exit()
86346                     .remove();
86347
86348                 extrasWrap = extrasWrap.enter()
86349                     .append('div')
86350                     .attr('class', 'structure-extras-wrap')
86351                     .merge(extrasWrap);
86352
86353                 var list = extrasWrap.selectAll('ul')
86354                     .data([0]);
86355
86356                 list = list.enter()
86357                     .append('ul')
86358                     .attr('class', 'rows')
86359                     .merge(list);
86360
86361
86362                 // Type
86363                 if (type) {
86364                     if (!typeField || typeField.id !== selected) {
86365                         typeField = uiField(context, type, _entityIDs, { wrap: false })
86366                             .on('change', changeType);
86367                     }
86368                     typeField.tags(tags);
86369                 } else {
86370                     typeField = null;
86371                 }
86372
86373                 var typeItem = list.selectAll('.structure-type-item')
86374                     .data(typeField ? [typeField] : [], function(d) { return d.id; });
86375
86376                 // Exit
86377                 typeItem.exit()
86378                     .remove();
86379
86380                 // Enter
86381                 var typeEnter = typeItem.enter()
86382                     .insert('li', ':first-child')
86383                     .attr('class', 'labeled-input structure-type-item');
86384
86385                 typeEnter
86386                     .append('span')
86387                     .attr('class', 'label structure-label-type')
86388                     .attr('for', 'preset-input-' + selected)
86389                     .text(_t('inspector.radio.structure.type'));
86390
86391                 typeEnter
86392                     .append('div')
86393                     .attr('class', 'structure-input-type-wrap');
86394
86395                 // Update
86396                 typeItem = typeItem
86397                     .merge(typeEnter);
86398
86399                 if (typeField) {
86400                     typeItem.selectAll('.structure-input-type-wrap')
86401                         .call(typeField.render);
86402                 }
86403
86404
86405                 // Layer
86406                 if (layer && showLayer) {
86407                     if (!layerField) {
86408                         layerField = uiField(context, layer, _entityIDs, { wrap: false })
86409                             .on('change', changeLayer);
86410                     }
86411                     layerField.tags(tags);
86412                     field.keys = utilArrayUnion(field.keys, ['layer']);
86413                 } else {
86414                     layerField = null;
86415                     field.keys = field.keys.filter(function(k) { return k !== 'layer'; });
86416                 }
86417
86418                 var layerItem = list.selectAll('.structure-layer-item')
86419                     .data(layerField ? [layerField] : []);
86420
86421                 // Exit
86422                 layerItem.exit()
86423                     .remove();
86424
86425                 // Enter
86426                 var layerEnter = layerItem.enter()
86427                     .append('li')
86428                     .attr('class', 'labeled-input structure-layer-item');
86429
86430                 layerEnter
86431                     .append('span')
86432                     .attr('class', 'label structure-label-layer')
86433                     .attr('for', 'preset-input-layer')
86434                     .text(_t('inspector.radio.structure.layer'));
86435
86436                 layerEnter
86437                     .append('div')
86438                     .attr('class', 'structure-input-layer-wrap');
86439
86440                 // Update
86441                 layerItem = layerItem
86442                     .merge(layerEnter);
86443
86444                 if (layerField) {
86445                     layerItem.selectAll('.structure-input-layer-wrap')
86446                         .call(layerField.render);
86447                 }
86448             }
86449
86450
86451             function changeType(t, onInput) {
86452                 var key = selectedKey();
86453                 if (!key) { return; }
86454
86455                 var val = t[key];
86456                 if (val !== 'no') {
86457                     _oldType[key] = val;
86458                 }
86459
86460                 if (field.type === 'structureRadio') {
86461                     // remove layer if it should not be set
86462                     if (val === 'no' ||
86463                         (key !== 'bridge' && key !== 'tunnel') ||
86464                         (key === 'tunnel' && val === 'building_passage')) {
86465                         t.layer = undefined;
86466                     }
86467                     // add layer if it should be set
86468                     if (t.layer === undefined) {
86469                         if (key === 'bridge' && val !== 'no') {
86470                             t.layer = '1';
86471                         }
86472                         if (key === 'tunnel' && val !== 'no' && val !== 'building_passage') {
86473                             t.layer = '-1';
86474                         }
86475                     }
86476                  }
86477
86478                 dispatch$1.call('change', this, t, onInput);
86479             }
86480
86481
86482             function changeLayer(t, onInput) {
86483                 if (t.layer === '0') {
86484                     t.layer = undefined;
86485                 }
86486                 dispatch$1.call('change', this, t, onInput);
86487             }
86488
86489
86490             function changeRadio() {
86491                 var t = {};
86492                 var activeKey;
86493
86494                 if (field.key) {
86495                     t[field.key] = undefined;
86496                 }
86497
86498                 radios.each(function(d) {
86499                     var active = select(this).property('checked');
86500                     if (active) { activeKey = d; }
86501
86502                     if (field.key) {
86503                         if (active) { t[field.key] = d; }
86504                     } else {
86505                         var val = _oldType[activeKey] || 'yes';
86506                         t[d] = active ? val : undefined;
86507                     }
86508                 });
86509
86510                 if (field.type === 'structureRadio') {
86511                     if (activeKey === 'bridge') {
86512                         t.layer = '1';
86513                     } else if (activeKey === 'tunnel' && t.tunnel !== 'building_passage') {
86514                         t.layer = '-1';
86515                     } else {
86516                         t.layer = undefined;
86517                     }
86518                 }
86519
86520                 dispatch$1.call('change', this, t);
86521             }
86522
86523
86524             radio.tags = function(tags) {
86525
86526                 radios.property('checked', function(d) {
86527                     if (field.key) {
86528                         return tags[field.key] === d;
86529                     }
86530                     return !!(typeof tags[d] === 'string' && tags[d].toLowerCase() !== 'no');
86531                 });
86532
86533                 function isMixed(d) {
86534                     if (field.key) {
86535                         return Array.isArray(tags[field.key]) && tags[field.key].includes(d);
86536                     }
86537                     return Array.isArray(tags[d]);
86538                 }
86539
86540                 labels
86541                     .classed('active', function(d) {
86542                         if (field.key) {
86543                             return (Array.isArray(tags[field.key]) && tags[field.key].includes(d))
86544                                 || tags[field.key] === d;
86545                         }
86546                         return Array.isArray(tags[d]) || !!(tags[d] && tags[d].toLowerCase() !== 'no');
86547                     })
86548                     .classed('mixed', isMixed)
86549                     .attr('title', function(d) {
86550                         return isMixed(d) ? _t('inspector.unshared_value_tooltip') : null;
86551                     });
86552
86553
86554                 var selection = radios.filter(function() { return this.checked; });
86555
86556                 if (selection.empty()) {
86557                     placeholder.text(_t('inspector.none'));
86558                 } else {
86559                     placeholder.text(selection.attr('value'));
86560                     _oldType[selection.datum()] = tags[selection.datum()];
86561                 }
86562
86563                 if (field.type === 'structureRadio') {
86564                     // For waterways without a tunnel tag, set 'culvert' as
86565                     // the _oldType to default to if the user picks 'tunnel'
86566                     if (!!tags.waterway && !_oldType.tunnel) {
86567                         _oldType.tunnel = 'culvert';
86568                     }
86569
86570                     wrap.call(structureExtras, tags);
86571                 }
86572             };
86573
86574
86575             radio.focus = function() {
86576                 radios.node().focus();
86577             };
86578
86579
86580             radio.entityIDs = function(val) {
86581                 if (!arguments.length) { return _entityIDs; }
86582                 _entityIDs = val;
86583                 _oldType = {};
86584                 return radio;
86585             };
86586
86587
86588             radio.isAllowed = function() {
86589                 return _entityIDs.length === 1;
86590             };
86591
86592
86593             return utilRebind(radio, dispatch$1, 'on');
86594         }
86595
86596         function uiFieldRestrictions(field, context) {
86597             var dispatch$1 = dispatch('change');
86598             var breathe = behaviorBreathe();
86599
86600             corePreferences('turn-restriction-via-way', null);                 // remove old key
86601             var storedViaWay = corePreferences('turn-restriction-via-way0');   // use new key #6922
86602             var storedDistance = corePreferences('turn-restriction-distance');
86603
86604             var _maxViaWay = storedViaWay !== null ? (+storedViaWay) : 0;
86605             var _maxDistance = storedDistance ? (+storedDistance) : 30;
86606             var _initialized = false;
86607             var _parent = select(null);       // the entire field
86608             var _container = select(null);    // just the map
86609             var _oldTurns;
86610             var _graph;
86611             var _vertexID;
86612             var _intersection;
86613             var _fromWayID;
86614
86615             var _lastXPos;
86616
86617
86618             function restrictions(selection) {
86619                 _parent = selection;
86620
86621                 // try to reuse the intersection, but always rebuild it if the graph has changed
86622                 if (_vertexID && (context.graph() !== _graph || !_intersection)) {
86623                     _graph = context.graph();
86624                     _intersection = osmIntersection(_graph, _vertexID, _maxDistance);
86625                 }
86626
86627                 // It's possible for there to be no actual intersection here.
86628                 // for example, a vertex of two `highway=path`
86629                 // In this case, hide the field.
86630                 var isOK = (
86631                     _intersection &&
86632                     _intersection.vertices.length &&           // has vertices
86633                     _intersection.vertices                     // has the vertex that the user selected
86634                         .filter(function(vertex) { return vertex.id === _vertexID; }).length &&
86635                     _intersection.ways.length > 2 &&           // has more than 2 ways
86636                     _intersection.ways                         // has more than 1 TO way
86637                         .filter(function(way) { return way.__to; }).length > 1
86638                 );
86639
86640                 // Also hide in the case where
86641                 select(selection.node().parentNode).classed('hide', !isOK);
86642
86643                 // if form field is hidden or has detached from dom, clean up.
86644                 if (!isOK ||
86645                     !context.container().select('.inspector-wrap.inspector-hidden').empty() ||
86646                     !selection.node().parentNode ||
86647                     !selection.node().parentNode.parentNode) {
86648                     selection.call(restrictions.off);
86649                     return;
86650                 }
86651
86652
86653                 var wrap = selection.selectAll('.form-field-input-wrap')
86654                     .data([0]);
86655
86656                 wrap = wrap.enter()
86657                     .append('div')
86658                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
86659                     .merge(wrap);
86660
86661                 var container = wrap.selectAll('.restriction-container')
86662                     .data([0]);
86663
86664                 // enter
86665                 var containerEnter = container.enter()
86666                     .append('div')
86667                     .attr('class', 'restriction-container');
86668
86669                 containerEnter
86670                     .append('div')
86671                     .attr('class', 'restriction-help');
86672
86673                 // update
86674                 _container = containerEnter
86675                     .merge(container)
86676                     .call(renderViewer);
86677
86678                 var controls = wrap.selectAll('.restriction-controls')
86679                     .data([0]);
86680
86681                 // enter/update
86682                 controls.enter()
86683                     .append('div')
86684                     .attr('class', 'restriction-controls-container')
86685                     .append('div')
86686                     .attr('class', 'restriction-controls')
86687                     .merge(controls)
86688                     .call(renderControls);
86689             }
86690
86691
86692             function renderControls(selection) {
86693                 var distControl = selection.selectAll('.restriction-distance')
86694                     .data([0]);
86695
86696                 distControl.exit()
86697                     .remove();
86698
86699                 var distControlEnter = distControl.enter()
86700                     .append('div')
86701                     .attr('class', 'restriction-control restriction-distance');
86702
86703                 distControlEnter
86704                     .append('span')
86705                     .attr('class', 'restriction-control-label restriction-distance-label')
86706                     .text(_t('restriction.controls.distance') + ':');
86707
86708                 distControlEnter
86709                     .append('input')
86710                     .attr('class', 'restriction-distance-input')
86711                     .attr('type', 'range')
86712                     .attr('min', '20')
86713                     .attr('max', '50')
86714                     .attr('step', '5');
86715
86716                 distControlEnter
86717                     .append('span')
86718                     .attr('class', 'restriction-distance-text');
86719
86720                 // update
86721                 selection.selectAll('.restriction-distance-input')
86722                     .property('value', _maxDistance)
86723                     .on('input', function() {
86724                         var val = select(this).property('value');
86725                         _maxDistance = +val;
86726                         _intersection = null;
86727                         _container.selectAll('.layer-osm .layer-turns *').remove();
86728                         corePreferences('turn-restriction-distance', _maxDistance);
86729                         _parent.call(restrictions);
86730                     });
86731
86732                 selection.selectAll('.restriction-distance-text')
86733                     .text(displayMaxDistance(_maxDistance));
86734
86735
86736                 var viaControl = selection.selectAll('.restriction-via-way')
86737                     .data([0]);
86738
86739                 viaControl.exit()
86740                     .remove();
86741
86742                 var viaControlEnter = viaControl.enter()
86743                     .append('div')
86744                     .attr('class', 'restriction-control restriction-via-way');
86745
86746                 viaControlEnter
86747                     .append('span')
86748                     .attr('class', 'restriction-control-label restriction-via-way-label')
86749                     .text(_t('restriction.controls.via') + ':');
86750
86751                 viaControlEnter
86752                     .append('input')
86753                     .attr('class', 'restriction-via-way-input')
86754                     .attr('type', 'range')
86755                     .attr('min', '0')
86756                     .attr('max', '2')
86757                     .attr('step', '1');
86758
86759                 viaControlEnter
86760                     .append('span')
86761                     .attr('class', 'restriction-via-way-text');
86762
86763                 // update
86764                 selection.selectAll('.restriction-via-way-input')
86765                     .property('value', _maxViaWay)
86766                     .on('input', function() {
86767                         var val = select(this).property('value');
86768                         _maxViaWay = +val;
86769                         _container.selectAll('.layer-osm .layer-turns *').remove();
86770                         corePreferences('turn-restriction-via-way0', _maxViaWay);
86771                         _parent.call(restrictions);
86772                     });
86773
86774                 selection.selectAll('.restriction-via-way-text')
86775                     .text(displayMaxVia(_maxViaWay));
86776             }
86777
86778
86779             function renderViewer(selection) {
86780                 if (!_intersection) { return; }
86781
86782                 var vgraph = _intersection.graph;
86783                 var filter = utilFunctor(true);
86784                 var projection = geoRawMercator();
86785
86786                 // Reflow warning: `utilGetDimensions` calls `getBoundingClientRect`
86787                 // Instead of asking the restriction-container for its dimensions,
86788                 //  we can ask the .sidebar, which can have its dimensions cached.
86789                 // width: calc as sidebar - padding
86790                 // height: hardcoded (from `80_app.css`)
86791                 // var d = utilGetDimensions(selection);
86792                 var sdims = utilGetDimensions(context.container().select('.sidebar'));
86793                 var d = [ sdims[0] - 50, 370 ];
86794                 var c = geoVecScale(d, 0.5);
86795                 var z = 22;
86796
86797                 projection.scale(geoZoomToScale(z));
86798
86799                 // Calculate extent of all key vertices
86800                 var extent = geoExtent();
86801                 for (var i = 0; i < _intersection.vertices.length; i++) {
86802                     extent._extend(_intersection.vertices[i].extent());
86803                 }
86804
86805                 // If this is a large intersection, adjust zoom to fit extent
86806                 if (_intersection.vertices.length > 1) {
86807                     var padding = 180;   // in z22 pixels
86808                     var tl = projection([extent[0][0], extent[1][1]]);
86809                     var br = projection([extent[1][0], extent[0][1]]);
86810                     var hFactor = (br[0] - tl[0]) / (d[0] - padding);
86811                     var vFactor = (br[1] - tl[1]) / (d[1] - padding);
86812                     var hZoomDiff = Math.log(Math.abs(hFactor)) / Math.LN2;
86813                     var vZoomDiff = Math.log(Math.abs(vFactor)) / Math.LN2;
86814                     z = z - Math.max(hZoomDiff, vZoomDiff);
86815                     projection.scale(geoZoomToScale(z));
86816                 }
86817
86818                 var padTop = 35;   // reserve top space for hint text
86819                 var extentCenter = projection(extent.center());
86820                 extentCenter[1] = extentCenter[1] - padTop;
86821
86822                 projection
86823                     .translate(geoVecSubtract(c, extentCenter))
86824                     .clipExtent([[0, 0], d]);
86825
86826                 var drawLayers = svgLayers(projection, context).only(['osm','touch']).dimensions(d);
86827                 var drawVertices = svgVertices(projection, context);
86828                 var drawLines = svgLines(projection, context);
86829                 var drawTurns = svgTurns(projection, context);
86830
86831                 var firstTime = selection.selectAll('.surface').empty();
86832
86833                 selection
86834                     .call(drawLayers);
86835
86836                 var surface = selection.selectAll('.surface')
86837                     .classed('tr', true);
86838
86839                 if (firstTime) {
86840                     _initialized = true;
86841
86842                     surface
86843                         .call(breathe);
86844                 }
86845
86846                 // This can happen if we've lowered the detail while a FROM way
86847                 // is selected, and that way is no longer part of the intersection.
86848                 if (_fromWayID && !vgraph.hasEntity(_fromWayID)) {
86849                     _fromWayID = null;
86850                     _oldTurns = null;
86851                 }
86852
86853                 surface
86854                     .call(utilSetDimensions, d)
86855                     .call(drawVertices, vgraph, _intersection.vertices, filter, extent, z)
86856                     .call(drawLines, vgraph, _intersection.ways, filter)
86857                     .call(drawTurns, vgraph, _intersection.turns(_fromWayID, _maxViaWay));
86858
86859                 surface
86860                     .on('click.restrictions', click)
86861                     .on('mouseover.restrictions', mouseover);
86862
86863                 surface
86864                     .selectAll('.selected')
86865                     .classed('selected', false);
86866
86867                 surface
86868                     .selectAll('.related')
86869                     .classed('related', false);
86870
86871                 if (_fromWayID) {
86872                     var way = vgraph.entity(_fromWayID);
86873                     surface
86874                         .selectAll('.' + _fromWayID)
86875                         .classed('selected', true)
86876                         .classed('related', true);
86877                 }
86878
86879                 document.addEventListener('resizeWindow', function () {
86880                     utilSetDimensions(_container, null);
86881                     redraw(1);
86882                 }, false);
86883
86884                 updateHints(null);
86885
86886
86887                 function click() {
86888                     surface
86889                         .call(breathe.off)
86890                         .call(breathe);
86891
86892                     var datum = event.target.__data__;
86893                     var entity = datum && datum.properties && datum.properties.entity;
86894                     if (entity) {
86895                         datum = entity;
86896                     }
86897
86898                     if (datum instanceof osmWay && (datum.__from || datum.__via)) {
86899                         _fromWayID = datum.id;
86900                         _oldTurns = null;
86901                         redraw();
86902
86903                     } else if (datum instanceof osmTurn) {
86904                         var actions, extraActions, turns, i;
86905                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
86906
86907                         if (datum.restrictionID && !datum.direct) {
86908                             return;
86909
86910                         } else if (datum.restrictionID && !datum.only) {    // NO -> ONLY
86911                             var seen = {};
86912                             var datumOnly = JSON.parse(JSON.stringify(datum));   // deep clone the datum
86913                             datumOnly.only = true;                               // but change this property
86914                             restrictionType = restrictionType.replace(/^no/, 'only');
86915
86916                             // Adding an ONLY restriction should destroy all other direct restrictions from the FROM towards the VIA.
86917                             // We will remember them in _oldTurns, and restore them if the user clicks again.
86918                             turns = _intersection.turns(_fromWayID, 2);
86919                             extraActions = [];
86920                             _oldTurns = [];
86921                             for (i = 0; i < turns.length; i++) {
86922                                 var turn = turns[i];
86923                                 if (seen[turn.restrictionID]) { continue; }  // avoid deleting the turn twice (#4968, #4928)
86924
86925                                 if (turn.direct && turn.path[1] === datum.path[1]) {
86926                                     seen[turns[i].restrictionID] = true;
86927                                     turn.restrictionType = osmInferRestriction(vgraph, turn, projection);
86928                                     _oldTurns.push(turn);
86929                                     extraActions.push(actionUnrestrictTurn(turn));
86930                                 }
86931                             }
86932
86933                             actions = _intersection.actions.concat(extraActions, [
86934                                 actionRestrictTurn(datumOnly, restrictionType),
86935                                 _t('operations.restriction.annotation.create')
86936                             ]);
86937
86938                         } else if (datum.restrictionID) {   // ONLY -> Allowed
86939                             // Restore whatever restrictions we might have destroyed by cycling thru the ONLY state.
86940                             // This relies on the assumption that the intersection was already split up when we
86941                             // performed the previous action (NO -> ONLY), so the IDs in _oldTurns shouldn't have changed.
86942                             turns = _oldTurns || [];
86943                             extraActions = [];
86944                             for (i = 0; i < turns.length; i++) {
86945                                 if (turns[i].key !== datum.key) {
86946                                     extraActions.push(actionRestrictTurn(turns[i], turns[i].restrictionType));
86947                                 }
86948                             }
86949                             _oldTurns = null;
86950
86951                             actions = _intersection.actions.concat(extraActions, [
86952                                 actionUnrestrictTurn(datum),
86953                                 _t('operations.restriction.annotation.delete')
86954                             ]);
86955
86956                         } else {    // Allowed -> NO
86957                             actions = _intersection.actions.concat([
86958                                 actionRestrictTurn(datum, restrictionType),
86959                                 _t('operations.restriction.annotation.create')
86960                             ]);
86961                         }
86962
86963                         context.perform.apply(context, actions);
86964
86965                         // At this point the datum will be changed, but will have same key..
86966                         // Refresh it and update the help..
86967                         var s = surface.selectAll('.' + datum.key);
86968                         datum = s.empty() ? null : s.datum();
86969                         updateHints(datum);
86970
86971                     } else {
86972                         _fromWayID = null;
86973                         _oldTurns = null;
86974                         redraw();
86975                     }
86976                 }
86977
86978
86979                 function mouseover() {
86980                     var datum = event.target.__data__;
86981                     updateHints(datum);
86982                 }
86983
86984                 _lastXPos = _lastXPos || sdims[0];
86985
86986                 function redraw(minChange) {
86987                     var xPos = -1;
86988
86989                     if (minChange) {
86990                         xPos = utilGetDimensions(context.container().select('.sidebar'))[0];
86991                     }
86992
86993                     if (!minChange || (minChange && Math.abs(xPos - _lastXPos) >= minChange)) {
86994                         if (context.hasEntity(_vertexID)) {
86995                             _lastXPos = xPos;
86996                             _container.call(renderViewer);
86997                         }
86998                     }
86999                 }
87000
87001
87002                 function highlightPathsFrom(wayID) {
87003                     surface.selectAll('.related')
87004                         .classed('related', false)
87005                         .classed('allow', false)
87006                         .classed('restrict', false)
87007                         .classed('only', false);
87008
87009                     surface.selectAll('.' + wayID)
87010                         .classed('related', true);
87011
87012                     if (wayID) {
87013                         var turns = _intersection.turns(wayID, _maxViaWay);
87014                         for (var i = 0; i < turns.length; i++) {
87015                             var turn = turns[i];
87016                             var ids = [turn.to.way];
87017                             var klass = (turn.no ? 'restrict' : (turn.only ? 'only' : 'allow'));
87018
87019                             if (turn.only || turns.length === 1) {
87020                                 if (turn.via.ways) {
87021                                     ids = ids.concat(turn.via.ways);
87022                                 }
87023                             } else if (turn.to.way === wayID) {
87024                                 continue;
87025                             }
87026
87027                             surface.selectAll(utilEntitySelector(ids))
87028                                 .classed('related', true)
87029                                 .classed('allow', (klass === 'allow'))
87030                                 .classed('restrict', (klass === 'restrict'))
87031                                 .classed('only', (klass === 'only'));
87032                         }
87033                     }
87034                 }
87035
87036
87037                 function updateHints(datum) {
87038                     var help = _container.selectAll('.restriction-help').html('');
87039
87040                     var placeholders = {};
87041                     ['from', 'via', 'to'].forEach(function(k) {
87042                         placeholders[k] = '<span class="qualifier">' + _t('restriction.help.' + k) + '</span>';
87043                     });
87044
87045                     var entity = datum && datum.properties && datum.properties.entity;
87046                     if (entity) {
87047                         datum = entity;
87048                     }
87049
87050                     if (_fromWayID) {
87051                         way = vgraph.entity(_fromWayID);
87052                         surface
87053                             .selectAll('.' + _fromWayID)
87054                             .classed('selected', true)
87055                             .classed('related', true);
87056                     }
87057
87058                     // Hovering a way
87059                     if (datum instanceof osmWay && datum.__from) {
87060                         way = datum;
87061
87062                         highlightPathsFrom(_fromWayID ? null : way.id);
87063                         surface.selectAll('.' + way.id)
87064                             .classed('related', true);
87065
87066                         var clickSelect = (!_fromWayID || _fromWayID !== way.id);
87067                         help
87068                             .append('div')      // "Click to select FROM {fromName}." / "FROM {fromName}"
87069                             .html(_t('restriction.help.' + (clickSelect ? 'select_from_name' : 'from_name'), {
87070                                 from: placeholders.from,
87071                                 fromName: displayName(way.id, vgraph)
87072                             }));
87073
87074
87075                     // Hovering a turn arrow
87076                     } else if (datum instanceof osmTurn) {
87077                         var restrictionType = osmInferRestriction(vgraph, datum, projection);
87078                         var turnType = restrictionType.replace(/^(only|no)\_/, '');
87079                         var indirect = (datum.direct === false ? _t('restriction.help.indirect') : '');
87080                         var klass, turnText, nextText;
87081
87082                         if (datum.no) {
87083                             klass = 'restrict';
87084                             turnText = _t('restriction.help.turn.no_' + turnType, { indirect: indirect });
87085                             nextText = _t('restriction.help.turn.only_' + turnType, { indirect: '' });
87086                         } else if (datum.only) {
87087                             klass = 'only';
87088                             turnText = _t('restriction.help.turn.only_' + turnType, { indirect: indirect });
87089                             nextText = _t('restriction.help.turn.allowed_' + turnType, { indirect: '' });
87090                         } else {
87091                             klass = 'allow';
87092                             turnText = _t('restriction.help.turn.allowed_' + turnType, { indirect: indirect });
87093                             nextText = _t('restriction.help.turn.no_' + turnType, { indirect: '' });
87094                         }
87095
87096                         help
87097                             .append('div')      // "NO Right Turn (indirect)"
87098                             .attr('class', 'qualifier ' + klass)
87099                             .text(turnText);
87100
87101                         help
87102                             .append('div')      // "FROM {fromName} TO {toName}"
87103                             .html(_t('restriction.help.from_name_to_name', {
87104                                 from: placeholders.from,
87105                                 fromName: displayName(datum.from.way, vgraph),
87106                                 to: placeholders.to,
87107                                 toName: displayName(datum.to.way, vgraph)
87108                             }));
87109
87110                         if (datum.via.ways && datum.via.ways.length) {
87111                             var names = [];
87112                             for (var i = 0; i < datum.via.ways.length; i++) {
87113                                 var prev = names[names.length - 1];
87114                                 var curr = displayName(datum.via.ways[i], vgraph);
87115                                 if (!prev || curr !== prev)   // collapse identical names
87116                                     { names.push(curr); }
87117                             }
87118
87119                             help
87120                                 .append('div')      // "VIA {viaNames}"
87121                                 .html(_t('restriction.help.via_names', {
87122                                     via: placeholders.via,
87123                                     viaNames: names.join(', ')
87124                                 }));
87125                         }
87126
87127                         if (!indirect) {
87128                             help
87129                                 .append('div')      // Click for "No Right Turn"
87130                                 .text(_t('restriction.help.toggle', { turn: nextText.trim() }));
87131                         }
87132
87133                         highlightPathsFrom(null);
87134                         var alongIDs = datum.path.slice();
87135                         surface.selectAll(utilEntitySelector(alongIDs))
87136                             .classed('related', true)
87137                             .classed('allow', (klass === 'allow'))
87138                             .classed('restrict', (klass === 'restrict'))
87139                             .classed('only', (klass === 'only'));
87140
87141
87142                     // Hovering empty surface
87143                     } else {
87144                         highlightPathsFrom(null);
87145                         if (_fromWayID) {
87146                             help
87147                                 .append('div')      // "FROM {fromName}"
87148                                 .html(_t('restriction.help.from_name', {
87149                                     from: placeholders.from,
87150                                     fromName: displayName(_fromWayID, vgraph)
87151                                 }));
87152
87153                         } else {
87154                             help
87155                                 .append('div')      // "Click to select a FROM segment."
87156                                 .html(_t('restriction.help.select_from', {
87157                                     from: placeholders.from
87158                                 }));
87159                         }
87160                     }
87161                 }
87162             }
87163
87164
87165             function displayMaxDistance(maxDist) {
87166                 var isImperial = !_mainLocalizer.usesMetric();
87167                 var opts;
87168
87169                 if (isImperial) {
87170                     var distToFeet = {   // imprecise conversion for prettier display
87171                         20: 70, 25: 85, 30: 100, 35: 115, 40: 130, 45: 145, 50: 160
87172                     }[maxDist];
87173                     opts = { distance: _t('units.feet', { quantity: distToFeet }) };
87174                 } else {
87175                     opts = { distance: _t('units.meters', { quantity: maxDist }) };
87176                 }
87177
87178                 return _t('restriction.controls.distance_up_to', opts);
87179             }
87180
87181
87182             function displayMaxVia(maxVia) {
87183                 return maxVia === 0 ? _t('restriction.controls.via_node_only')
87184                     : maxVia === 1 ? _t('restriction.controls.via_up_to_one')
87185                     : _t('restriction.controls.via_up_to_two');
87186             }
87187
87188
87189             function displayName(entityID, graph) {
87190                 var entity = graph.entity(entityID);
87191                 var name = utilDisplayName(entity) || '';
87192                 var matched = _mainPresetIndex.match(entity, graph);
87193                 var type = (matched && matched.name()) || utilDisplayType(entity.id);
87194                 return name || type;
87195             }
87196
87197
87198             restrictions.entityIDs = function(val) {
87199                 _intersection = null;
87200                 _fromWayID = null;
87201                 _oldTurns = null;
87202                 _vertexID = val[0];
87203             };
87204
87205
87206             restrictions.tags = function() {};
87207             restrictions.focus = function() {};
87208
87209
87210             restrictions.off = function(selection) {
87211                 if (!_initialized) { return; }
87212
87213                 selection.selectAll('.surface')
87214                     .call(breathe.off)
87215                     .on('click.restrictions', null)
87216                     .on('mouseover.restrictions', null);
87217
87218                 select(window)
87219                     .on('resize.restrictions', null);
87220             };
87221
87222
87223             return utilRebind(restrictions, dispatch$1, 'on');
87224         }
87225
87226         uiFieldRestrictions.supportsMultiselection = false;
87227
87228         function uiFieldTextarea(field, context) {
87229             var dispatch$1 = dispatch('change');
87230             var input = select(null);
87231             var _tags;
87232
87233
87234             function textarea(selection) {
87235                 var wrap = selection.selectAll('.form-field-input-wrap')
87236                     .data([0]);
87237
87238                 wrap = wrap.enter()
87239                     .append('div')
87240                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87241                     .merge(wrap);
87242
87243                 input = wrap.selectAll('textarea')
87244                     .data([0]);
87245
87246                 input = input.enter()
87247                     .append('textarea')
87248                     .attr('id', field.domId)
87249                     .call(utilNoAuto)
87250                     .on('input', change(true))
87251                     .on('blur', change())
87252                     .on('change', change())
87253                     .merge(input);
87254             }
87255
87256
87257             function change(onInput) {
87258                 return function() {
87259
87260                     var val = utilGetSetValue(input);
87261                     if (!onInput) { val = context.cleanTagValue(val); }
87262
87263                     // don't override multiple values with blank string
87264                     if (!val && Array.isArray(_tags[field.key])) { return; }
87265
87266                     var t = {};
87267                     t[field.key] = val || undefined;
87268                     dispatch$1.call('change', this, t, onInput);
87269                 };
87270             }
87271
87272
87273             textarea.tags = function(tags) {
87274                 _tags = tags;
87275
87276                 var isMixed = Array.isArray(tags[field.key]);
87277
87278                 utilGetSetValue(input, !isMixed && tags[field.key] ? tags[field.key] : '')
87279                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : undefined)
87280                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : (field.placeholder() || _t('inspector.unknown')))
87281                     .classed('mixed', isMixed);
87282             };
87283
87284
87285             textarea.focus = function() {
87286                 input.node().focus();
87287             };
87288
87289
87290             return utilRebind(textarea, dispatch$1, 'on');
87291         }
87292
87293         function uiFieldWikidata(field, context) {
87294             var wikidata = services.wikidata;
87295             var dispatch$1 = dispatch('change');
87296
87297             var _selection = select(null);
87298             var _searchInput = select(null);
87299             var _qid = null;
87300             var _wikidataEntity = null;
87301             var _wikiURL = '';
87302             var _entityIDs = [];
87303
87304             var _wikipediaKey = field.keys && field.keys.find(function(key) {
87305                     return key.includes('wikipedia');
87306                 }),
87307                 _hintKey = field.key === 'wikidata' ? 'name' : field.key.split(':')[0];
87308
87309             var combobox = uiCombobox(context, 'combo-' + field.safeid)
87310                 .caseSensitive(true)
87311                 .minItems(1);
87312
87313             function wiki(selection) {
87314
87315                 _selection = selection;
87316
87317                 var wrap = selection.selectAll('.form-field-input-wrap')
87318                     .data([0]);
87319
87320                 wrap = wrap.enter()
87321                     .append('div')
87322                     .attr('class', 'form-field-input-wrap form-field-input-' + field.type)
87323                     .merge(wrap);
87324
87325
87326                 var list = wrap.selectAll('ul')
87327                     .data([0]);
87328
87329                 list = list.enter()
87330                     .append('ul')
87331                     .attr('class', 'rows')
87332                     .merge(list);
87333
87334                 var searchRow = list.selectAll('li.wikidata-search')
87335                     .data([0]);
87336
87337                 var searchRowEnter = searchRow.enter()
87338                     .append('li')
87339                     .attr('class', 'wikidata-search');
87340
87341                 searchRowEnter
87342                     .append('input')
87343                     .attr('type', 'text')
87344                     .attr('id', field.domId)
87345                     .style('flex', '1')
87346                     .call(utilNoAuto)
87347                     .on('focus', function() {
87348                         var node = select(this).node();
87349                         node.setSelectionRange(0, node.value.length);
87350                     })
87351                     .on('blur', function() {
87352                         setLabelForEntity();
87353                     })
87354                     .call(combobox.fetcher(fetchWikidataItems));
87355
87356                 combobox.on('accept', function(d) {
87357                     _qid = d.id;
87358                     change();
87359                 }).on('cancel', function() {
87360                     setLabelForEntity();
87361                 });
87362
87363                 searchRowEnter
87364                     .append('button')
87365                     .attr('class', 'form-field-button wiki-link')
87366                     .attr('title', _t('icons.view_on', { domain: 'wikidata.org' }))
87367                     .attr('tabindex', -1)
87368                     .call(svgIcon('#iD-icon-out-link'))
87369                     .on('click', function() {
87370                         event.preventDefault();
87371                         if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87372                     });
87373
87374                 searchRow = searchRow.merge(searchRowEnter);
87375
87376                 _searchInput = searchRow.select('input');
87377
87378                 var wikidataProperties = ['description', 'identifier'];
87379
87380                 var items = list.selectAll('li.labeled-input')
87381                     .data(wikidataProperties);
87382
87383                 // Enter
87384                 var enter = items.enter()
87385                     .append('li')
87386                     .attr('class', function(d) { return 'labeled-input preset-wikidata-' + d; });
87387
87388                 enter
87389                     .append('span')
87390                     .attr('class', 'label')
87391                     .text(function(d) { return _t('wikidata.' + d); });
87392
87393                 enter
87394                     .append('input')
87395                     .attr('type', 'text')
87396                     .call(utilNoAuto)
87397                     .classed('disabled', 'true')
87398                     .attr('readonly', 'true');
87399
87400                 enter
87401                     .append('button')
87402                     .attr('class', 'form-field-button')
87403                     .attr('title', _t('icons.copy'))
87404                     .attr('tabindex', -1)
87405                     .call(svgIcon('#iD-operation-copy'))
87406                     .on('click', function() {
87407                         event.preventDefault();
87408                         select(this.parentNode)
87409                             .select('input')
87410                             .node()
87411                             .select();
87412                         document.execCommand('copy');
87413                     });
87414
87415             }
87416
87417             function fetchWikidataItems(q, callback) {
87418
87419                 if (!q && _hintKey) {
87420                     // other tags may be good search terms
87421                     for (var i in _entityIDs) {
87422                         var entity = context.hasEntity(_entityIDs[i]);
87423                         if (entity.tags[_hintKey]) {
87424                             q = entity.tags[_hintKey];
87425                             break;
87426                         }
87427                     }
87428                 }
87429
87430                 wikidata.itemsForSearchQuery(q, function(err, data) {
87431                     if (err) { return; }
87432
87433                     for (var i in data) {
87434                         data[i].value = data[i].label + ' (' +  data[i].id + ')';
87435                         data[i].title = data[i].description;
87436                     }
87437
87438                     if (callback) { callback(data); }
87439                 });
87440             }
87441
87442
87443             function change() {
87444                 var syncTags = {};
87445                 syncTags[field.key] = _qid;
87446                 dispatch$1.call('change', this, syncTags);
87447
87448                 // attempt asynchronous update of wikidata tag..
87449                 var initGraph = context.graph();
87450                 var initEntityIDs = _entityIDs;
87451
87452                 wikidata.entityByQID(_qid, function(err, entity) {
87453                     if (err) { return; }
87454
87455                     // If graph has changed, we can't apply this update.
87456                     if (context.graph() !== initGraph) { return; }
87457
87458                     if (!entity.sitelinks) { return; }
87459
87460                     var langs = wikidata.languagesToQuery();
87461                     // use the label and description languages as fallbacks
87462                     ['labels', 'descriptions'].forEach(function(key) {
87463                         if (!entity[key]) { return; }
87464
87465                         var valueLangs = Object.keys(entity[key]);
87466                         if (valueLangs.length === 0) { return; }
87467                         var valueLang = valueLangs[0];
87468
87469                         if (langs.indexOf(valueLang) === -1) {
87470                             langs.push(valueLang);
87471                         }
87472                     });
87473
87474                     var newWikipediaValue;
87475
87476                     if (_wikipediaKey) {
87477                         var foundPreferred;
87478                         for (var i in langs) {
87479                             var lang = langs[i];
87480                             var siteID = lang.replace('-', '_') + 'wiki';
87481                             if (entity.sitelinks[siteID]) {
87482                                 foundPreferred = true;
87483                                 newWikipediaValue = lang + ':' + entity.sitelinks[siteID].title;
87484                                 // use the first match
87485                                 break;
87486                             }
87487                         }
87488
87489                         if (!foundPreferred) {
87490                             // No wikipedia sites available in the user's language or the fallback languages,
87491                             // default to any wikipedia sitelink
87492
87493                             var wikiSiteKeys = Object.keys(entity.sitelinks).filter(function(site) {
87494                                 return site.endsWith('wiki');
87495                             });
87496
87497                             if (wikiSiteKeys.length === 0) {
87498                                 // if no wikipedia pages are linked to this wikidata entity, delete that tag
87499                                 newWikipediaValue = null;
87500                             } else {
87501                                 var wikiLang = wikiSiteKeys[0].slice(0, -4).replace('_', '-');
87502                                 var wikiTitle = entity.sitelinks[wikiSiteKeys[0]].title;
87503                                 newWikipediaValue = wikiLang + ':' + wikiTitle;
87504                             }
87505                         }
87506                     }
87507
87508                     if (newWikipediaValue) {
87509                         newWikipediaValue = context.cleanTagValue(newWikipediaValue);
87510                     }
87511
87512                     if (typeof newWikipediaValue === 'undefined') { return; }
87513
87514                     var actions = initEntityIDs.map(function(entityID) {
87515                         var entity = context.hasEntity(entityID);
87516                         if (!entity) { return; }
87517
87518                         var currTags = Object.assign({}, entity.tags);  // shallow copy
87519                         if (newWikipediaValue === null) {
87520                             if (!currTags[_wikipediaKey]) { return; }
87521
87522                             delete currTags[_wikipediaKey];
87523                         } else {
87524                             currTags[_wikipediaKey] = newWikipediaValue;
87525                         }
87526
87527                         return actionChangeTags(entityID, currTags);
87528                     }).filter(Boolean);
87529
87530                     if (!actions.length) { return; }
87531
87532                     // Coalesce the update of wikidata tag into the previous tag change
87533                     context.overwrite(
87534                         function actionUpdateWikipediaTags(graph) {
87535                             actions.forEach(function(action) {
87536                                 graph = action(graph);
87537                             });
87538                             return graph;
87539                         },
87540                         context.history().undoAnnotation()
87541                     );
87542
87543                     // do not dispatch.call('change') here, because entity_editor
87544                     // changeTags() is not intended to be called asynchronously
87545                 });
87546             }
87547
87548             function setLabelForEntity() {
87549                 var label = '';
87550                 if (_wikidataEntity) {
87551                     label = entityPropertyForDisplay(_wikidataEntity, 'labels');
87552                     if (label.length === 0) {
87553                         label = _wikidataEntity.id.toString();
87554                     }
87555                 }
87556                 utilGetSetValue(_searchInput, label);
87557             }
87558
87559
87560             wiki.tags = function(tags) {
87561
87562                 var isMixed = Array.isArray(tags[field.key]);
87563                 _searchInput
87564                     .attr('title', isMixed ? tags[field.key].filter(Boolean).join('\n') : null)
87565                     .attr('placeholder', isMixed ? _t('inspector.multiple_values') : '')
87566                     .classed('mixed', isMixed);
87567
87568                 _qid = typeof tags[field.key] === 'string' && tags[field.key] || '';
87569
87570                 if (!/^Q[0-9]*$/.test(_qid)) {   // not a proper QID
87571                     unrecognized();
87572                     return;
87573                 }
87574
87575                 // QID value in correct format
87576                 _wikiURL = 'https://wikidata.org/wiki/' + _qid;
87577                 wikidata.entityByQID(_qid, function(err, entity) {
87578                     if (err) {
87579                         unrecognized();
87580                         return;
87581                     }
87582                     _wikidataEntity = entity;
87583
87584                     setLabelForEntity();
87585
87586                     var description = entityPropertyForDisplay(entity, 'descriptions');
87587
87588                     _selection.select('button.wiki-link')
87589                         .classed('disabled', false);
87590
87591                     _selection.select('.preset-wikidata-description')
87592                         .style('display', function(){
87593                             return description.length > 0 ? 'flex' : 'none';
87594                         })
87595                         .select('input')
87596                         .attr('value', description);
87597
87598                     _selection.select('.preset-wikidata-identifier')
87599                         .style('display', function(){
87600                             return entity.id ? 'flex' : 'none';
87601                         })
87602                         .select('input')
87603                         .attr('value', entity.id);
87604                 });
87605
87606
87607                 // not a proper QID
87608                 function unrecognized() {
87609                     _wikidataEntity = null;
87610                     setLabelForEntity();
87611
87612                     _selection.select('.preset-wikidata-description')
87613                         .style('display', 'none');
87614                     _selection.select('.preset-wikidata-identifier')
87615                         .style('display', 'none');
87616
87617                     _selection.select('button.wiki-link')
87618                         .classed('disabled', true);
87619
87620                     if (_qid && _qid !== '') {
87621                         _wikiURL = 'https://wikidata.org/wiki/Special:Search?search=' + _qid;
87622                     } else {
87623                         _wikiURL = '';
87624                     }
87625                 }
87626             };
87627
87628             function entityPropertyForDisplay(wikidataEntity, propKey) {
87629                 if (!wikidataEntity[propKey]) { return ''; }
87630                 var propObj = wikidataEntity[propKey];
87631                 var langKeys = Object.keys(propObj);
87632                 if (langKeys.length === 0) { return ''; }
87633                 // sorted by priority, since we want to show the user's language first if possible
87634                 var langs = wikidata.languagesToQuery();
87635                 for (var i in langs) {
87636                     var lang = langs[i];
87637                     var valueObj = propObj[lang];
87638                     if (valueObj && valueObj.value && valueObj.value.length > 0) { return valueObj.value; }
87639                 }
87640                 // default to any available value
87641                 return propObj[langKeys[0]].value;
87642             }
87643
87644
87645             wiki.entityIDs = function(val) {
87646                 if (!arguments.length) { return _entityIDs; }
87647                 _entityIDs = val;
87648                 return wiki;
87649             };
87650
87651
87652             wiki.focus = function() {
87653                 _searchInput.node().focus();
87654             };
87655
87656
87657             return utilRebind(wiki, dispatch$1, 'on');
87658         }
87659
87660         function uiFieldWikipedia(field, context) {
87661           var arguments$1 = arguments;
87662
87663           var dispatch$1 = dispatch('change');
87664           var wikipedia = services.wikipedia;
87665           var wikidata = services.wikidata;
87666           var _langInput = select(null);
87667           var _titleInput = select(null);
87668           var _wikiURL = '';
87669           var _entityIDs;
87670           var _tags;
87671
87672           var _dataWikipedia = [];
87673           _mainFileFetcher.get('wmf_sitematrix')
87674             .then(function (d) {
87675               _dataWikipedia = d;
87676               if (_tags) { updateForTags(_tags); }
87677             })
87678             .catch(function () { /* ignore */ });
87679
87680
87681           var langCombo = uiCombobox(context, 'wikipedia-lang')
87682             .fetcher(function (value, callback) {
87683               var v = value.toLowerCase();
87684               callback(_dataWikipedia
87685                 .filter(function (d) {
87686                   return d[0].toLowerCase().indexOf(v) >= 0 ||
87687                     d[1].toLowerCase().indexOf(v) >= 0 ||
87688                     d[2].toLowerCase().indexOf(v) >= 0;
87689                 })
87690                 .map(function (d) { return ({ value: d[1] }); })
87691               );
87692             });
87693
87694           var titleCombo = uiCombobox(context, 'wikipedia-title')
87695             .fetcher(function (value, callback) {
87696               if (!value) {
87697                 value = '';
87698                 for (var i in _entityIDs) {
87699                   var entity = context.hasEntity(_entityIDs[i]);
87700                   if (entity.tags.name) {
87701                     value = entity.tags.name;
87702                     break;
87703                   }
87704                 }
87705               }
87706               var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions;
87707               searchfn(language()[2], value, function (query, data) {
87708                 callback( data.map(function (d) { return ({ value: d }); }) );
87709               });
87710             });
87711
87712
87713           function wiki(selection) {
87714             var wrap = selection.selectAll('.form-field-input-wrap')
87715               .data([0]);
87716
87717             wrap = wrap.enter()
87718               .append('div')
87719               .attr('class', ("form-field-input-wrap form-field-input-" + (field.type)))
87720               .merge(wrap);
87721
87722
87723             var langContainer = wrap.selectAll('.wiki-lang-container')
87724               .data([0]);
87725
87726             langContainer = langContainer.enter()
87727               .append('div')
87728               .attr('class', 'wiki-lang-container')
87729               .merge(langContainer);
87730
87731
87732             _langInput = langContainer.selectAll('input.wiki-lang')
87733               .data([0]);
87734
87735             _langInput = _langInput.enter()
87736               .append('input')
87737               .attr('type', 'text')
87738               .attr('class', 'wiki-lang')
87739               .attr('placeholder', _t('translate.localized_translation_language'))
87740               .call(utilNoAuto)
87741               .call(langCombo)
87742               .merge(_langInput);
87743
87744             utilGetSetValue(_langInput, language()[1]);
87745
87746             _langInput
87747               .on('blur', changeLang)
87748               .on('change', changeLang);
87749
87750
87751             var titleContainer = wrap.selectAll('.wiki-title-container')
87752               .data([0]);
87753
87754             titleContainer = titleContainer.enter()
87755               .append('div')
87756               .attr('class', 'wiki-title-container')
87757               .merge(titleContainer);
87758
87759             _titleInput = titleContainer.selectAll('input.wiki-title')
87760               .data([0]);
87761
87762             _titleInput = _titleInput.enter()
87763               .append('input')
87764               .attr('type', 'text')
87765               .attr('class', 'wiki-title')
87766               .attr('id', field.domId)
87767               .call(utilNoAuto)
87768               .call(titleCombo)
87769               .merge(_titleInput);
87770
87771             _titleInput
87772               .on('blur', blur)
87773               .on('change', change);
87774
87775
87776             var link = titleContainer.selectAll('.wiki-link')
87777               .data([0]);
87778
87779             link = link.enter()
87780               .append('button')
87781               .attr('class', 'form-field-button wiki-link')
87782               .attr('tabindex', -1)
87783               .attr('title', _t('icons.view_on', { domain: 'wikipedia.org' }))
87784               .call(svgIcon('#iD-icon-out-link'))
87785               .merge(link);
87786
87787             link
87788               .on('click', function () {
87789                 event.preventDefault();
87790                 if (_wikiURL) { window.open(_wikiURL, '_blank'); }
87791               });
87792           }
87793
87794
87795           function language() {
87796             var value = utilGetSetValue(_langInput).toLowerCase();
87797             var locale = _mainLocalizer.localeCode().toLowerCase();
87798             var localeLanguage;
87799             return _dataWikipedia.find(function (d) {
87800               if (d[2] === locale) { localeLanguage = d; }
87801               return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value;
87802             }) || localeLanguage || ['English', 'English', 'en'];
87803           }
87804
87805
87806           function changeLang() {
87807             utilGetSetValue(_langInput, language()[1]);
87808             change(true);
87809           }
87810
87811
87812           function blur() {
87813             change(true);
87814           }
87815
87816
87817           function change(skipWikidata) {
87818             var value = utilGetSetValue(_titleInput);
87819             var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/);
87820             var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
87821             var syncTags = {};
87822
87823             if (l) {
87824               // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization
87825               value = decodeURIComponent(m[2]).replace(/_/g, ' ');
87826               if (m[3]) {
87827                 var anchor;
87828                 // try {
87829                 // leave this out for now - #6232
87830                   // Best-effort `anchordecode:` implementation
87831                   // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1'));
87832                 // } catch (e) {
87833                 anchor = decodeURIComponent(m[3]);
87834                 // }
87835                 value += '#' + anchor.replace(/_/g, ' ');
87836               }
87837               value = value.slice(0, 1).toUpperCase() + value.slice(1);
87838               utilGetSetValue(_langInput, l[1]);
87839               utilGetSetValue(_titleInput, value);
87840             }
87841
87842             if (value) {
87843               syncTags.wikipedia = context.cleanTagValue(language()[2] + ':' + value);
87844             } else {
87845               syncTags.wikipedia = undefined;
87846             }
87847
87848             dispatch$1.call('change', this, syncTags);
87849
87850
87851             if (skipWikidata || !value || !language()[2]) { return; }
87852
87853             // attempt asynchronous update of wikidata tag..
87854             var initGraph = context.graph();
87855             var initEntityIDs = _entityIDs;
87856
87857             wikidata.itemsByTitle(language()[2], value, function (err, data) {
87858               if (err || !data || !Object.keys(data).length) { return; }
87859
87860               // If graph has changed, we can't apply this update.
87861               if (context.graph() !== initGraph) { return; }
87862
87863               var qids = Object.keys(data);
87864               var value = qids && qids.find(function (id) { return id.match(/^Q\d+$/); });
87865
87866               var actions = initEntityIDs.map(function (entityID) {
87867                 var entity = context.entity(entityID).tags;
87868                 var currTags = Object.assign({}, entity);  // shallow copy
87869                 if (currTags.wikidata !== value) {
87870                     currTags.wikidata = value;
87871                     return actionChangeTags(entityID, currTags);
87872                 }
87873               }).filter(Boolean);
87874
87875               if (!actions.length) { return; }
87876
87877               // Coalesce the update of wikidata tag into the previous tag change
87878               context.overwrite(
87879                 function actionUpdateWikidataTags(graph) {
87880                   actions.forEach(function(action) {
87881                     graph = action(graph);
87882                   });
87883                   return graph;
87884                 },
87885                 context.history().undoAnnotation()
87886               );
87887
87888               // do not dispatch.call('change') here, because entity_editor
87889               // changeTags() is not intended to be called asynchronously
87890             });
87891           }
87892
87893
87894           wiki.tags = function (tags) {
87895             _tags = tags;
87896             updateForTags(tags);
87897           };
87898
87899           function updateForTags(tags) {
87900
87901             var value = typeof tags[field.key] === 'string' ? tags[field.key] : '';
87902             var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/);
87903             var l = m && _dataWikipedia.find(function (d) { return m[1] === d[2]; });
87904             var anchor = m && m[3];
87905
87906             // value in correct format
87907             if (l) {
87908               utilGetSetValue(_langInput, l[1]);
87909               utilGetSetValue(_titleInput, m[2] + (anchor ? ('#' + anchor) : ''));
87910               if (anchor) {
87911                 try {
87912                   // Best-effort `anchorencode:` implementation
87913                   anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.');
87914                 } catch (e) {
87915                   anchor = anchor.replace(/ /g, '_');
87916                 }
87917               }
87918               _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' +
87919                 m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : '');
87920
87921             // unrecognized value format
87922             } else {
87923               utilGetSetValue(_titleInput, value);
87924               if (value && value !== '') {
87925                 utilGetSetValue(_langInput, '');
87926                 _wikiURL = "https://en.wikipedia.org/wiki/Special:Search?search=" + value;
87927               } else {
87928                 _wikiURL = '';
87929               }
87930             }
87931           }
87932
87933
87934           wiki.entityIDs = function (val) {
87935             if (!arguments$1.length) { return _entityIDs; }
87936             _entityIDs = val;
87937             return wiki;
87938           };
87939
87940
87941           wiki.focus = function () {
87942             _titleInput.node().focus();
87943           };
87944
87945
87946           return utilRebind(wiki, dispatch$1, 'on');
87947         }
87948
87949         uiFieldWikipedia.supportsMultiselection = false;
87950
87951         var uiFields = {
87952             access: uiFieldAccess,
87953             address: uiFieldAddress,
87954             check: uiFieldCheck,
87955             combo: uiFieldCombo,
87956             cycleway: uiFieldCycleway,
87957             defaultCheck: uiFieldCheck,
87958             email: uiFieldText,
87959             identifier: uiFieldText,
87960             lanes: uiFieldLanes,
87961             localized: uiFieldLocalized,
87962             maxspeed: uiFieldMaxspeed,
87963             multiCombo: uiFieldCombo,
87964             networkCombo: uiFieldCombo,
87965             number: uiFieldText,
87966             onewayCheck: uiFieldCheck,
87967             radio: uiFieldRadio,
87968             restrictions: uiFieldRestrictions,
87969             semiCombo: uiFieldCombo,
87970             structureRadio: uiFieldRadio,
87971             tel: uiFieldText,
87972             text: uiFieldText,
87973             textarea: uiFieldTextarea,
87974             typeCombo: uiFieldCombo,
87975             url: uiFieldText,
87976             wikidata: uiFieldWikidata,
87977             wikipedia: uiFieldWikipedia
87978         };
87979
87980         function uiField(context, presetField, entityIDs, options) {
87981             options = Object.assign({
87982                 show: true,
87983                 wrap: true,
87984                 remove: true,
87985                 revert: true,
87986                 info: true
87987             }, options);
87988
87989             var dispatch$1 = dispatch('change', 'revert');
87990             var field = Object.assign({}, presetField);   // shallow copy
87991             field.domId = utilUniqueDomId('form-field-' + field.safeid);
87992             var _show = options.show;
87993             var _state = '';
87994             var _tags = {};
87995
87996             var _locked = false;
87997             var _lockedTip = uiTooltip()
87998                 .title(_t('inspector.lock.suggestion', { label: field.label }))
87999                 .placement('bottom');
88000
88001
88002             field.keys = field.keys || [field.key];
88003
88004             // only create the fields that are actually being shown
88005             if (_show && !field.impl) {
88006                 createField();
88007             }
88008
88009             // Creates the field.. This is done lazily,
88010             // once we know that the field will be shown.
88011             function createField() {
88012                 field.impl = uiFields[field.type](field, context)
88013                     .on('change', function(t, onInput) {
88014                         dispatch$1.call('change', field, t, onInput);
88015                     });
88016
88017                 if (entityIDs) {
88018                     field.entityIDs = entityIDs;
88019                     // if this field cares about the entities, pass them along
88020                     if (field.impl.entityIDs) {
88021                         field.impl.entityIDs(entityIDs);
88022                     }
88023                 }
88024             }
88025
88026
88027             function isModified() {
88028                 if (!entityIDs || !entityIDs.length) { return false; }
88029                 return entityIDs.some(function(entityID) {
88030                     var original = context.graph().base().entities[entityID];
88031                     var latest = context.graph().entity(entityID);
88032                     return field.keys.some(function(key) {
88033                         return original ? latest.tags[key] !== original.tags[key] : latest.tags[key];
88034                     });
88035                 });
88036             }
88037
88038
88039             function tagsContainFieldKey() {
88040                 return field.keys.some(function(key) {
88041                     if (field.type === 'multiCombo') {
88042                         for (var tagKey in _tags) {
88043                             if (tagKey.indexOf(key) === 0) {
88044                                 return true;
88045                             }
88046                         }
88047                         return false;
88048                     }
88049                     return _tags[key] !== undefined;
88050                 });
88051             }
88052
88053
88054             function revert(d) {
88055                 event.stopPropagation();
88056                 event.preventDefault();
88057                 if (!entityIDs || _locked) { return; }
88058
88059                 dispatch$1.call('revert', d, d.keys);
88060             }
88061
88062
88063             function remove(d) {
88064                 event.stopPropagation();
88065                 event.preventDefault();
88066                 if (_locked) { return; }
88067
88068                 var t = {};
88069                 d.keys.forEach(function(key) {
88070                     t[key] = undefined;
88071                 });
88072
88073                 dispatch$1.call('change', d, t);
88074             }
88075
88076
88077             field.render = function(selection) {
88078                 var container = selection.selectAll('.form-field')
88079                     .data([field]);
88080
88081                 // Enter
88082                 var enter = container.enter()
88083                     .append('div')
88084                     .attr('class', function(d) { return 'form-field form-field-' + d.safeid; })
88085                     .classed('nowrap', !options.wrap);
88086
88087                 if (options.wrap) {
88088                     var labelEnter = enter
88089                         .append('label')
88090                         .attr('class', 'field-label')
88091                         .attr('for', function(d) { return d.domId; });
88092
88093                     var textEnter = labelEnter
88094                         .append('span')
88095                         .attr('class', 'label-text');
88096
88097                     textEnter
88098                         .append('span')
88099                         .attr('class', 'label-textvalue')
88100                         .text(function(d) { return d.label(); });
88101
88102                     textEnter
88103                         .append('span')
88104                         .attr('class', 'label-textannotation');
88105
88106                     if (options.remove) {
88107                         labelEnter
88108                             .append('button')
88109                             .attr('class', 'remove-icon')
88110                             .attr('title', _t('icons.remove'))
88111                             .attr('tabindex', -1)
88112                             .call(svgIcon('#iD-operation-delete'));
88113                     }
88114
88115                     if (options.revert) {
88116                         labelEnter
88117                             .append('button')
88118                             .attr('class', 'modified-icon')
88119                             .attr('title', _t('icons.undo'))
88120                             .attr('tabindex', -1)
88121                             .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-redo' : '#iD-icon-undo'));
88122                     }
88123                 }
88124
88125
88126                 // Update
88127                 container = container
88128                     .merge(enter);
88129
88130                 container.select('.field-label > .remove-icon')  // propagate bound data
88131                     .on('click', remove);
88132
88133                 container.select('.field-label > .modified-icon')  // propagate bound data
88134                     .on('click', revert);
88135
88136                 container
88137                     .each(function(d) {
88138                         var selection = select(this);
88139
88140                         if (!d.impl) {
88141                             createField();
88142                         }
88143
88144                         var reference, help;
88145
88146                         // instantiate field help
88147                         if (options.wrap && field.type === 'restrictions') {
88148                             help = uiFieldHelp(context, 'restrictions');
88149                         }
88150
88151                         // instantiate tag reference
88152                         if (options.wrap && options.info) {
88153                             var referenceKey = d.key;
88154                             if (d.type === 'multiCombo') {   // lookup key without the trailing ':'
88155                                 referenceKey = referenceKey.replace(/:$/, '');
88156                             }
88157
88158                             reference = uiTagReference(d.reference || { key: referenceKey });
88159                             if (_state === 'hover') {
88160                                 reference.showing(false);
88161                             }
88162                         }
88163
88164                         selection
88165                             .call(d.impl);
88166
88167                         // add field help components
88168                         if (help) {
88169                             selection
88170                                 .call(help.body)
88171                                 .select('.field-label')
88172                                 .call(help.button);
88173                         }
88174
88175                         // add tag reference components
88176                         if (reference) {
88177                             selection
88178                                 .call(reference.body)
88179                                 .select('.field-label')
88180                                 .call(reference.button);
88181                         }
88182
88183                         d.impl.tags(_tags);
88184                     });
88185
88186
88187                     container
88188                         .classed('locked', _locked)
88189                         .classed('modified', isModified())
88190                         .classed('present', tagsContainFieldKey());
88191
88192
88193                     // show a tip and lock icon if the field is locked
88194                     var annotation = container.selectAll('.field-label .label-textannotation');
88195                     var icon = annotation.selectAll('.icon')
88196                         .data(_locked ? [0]: []);
88197
88198                     icon.exit()
88199                         .remove();
88200
88201                     icon.enter()
88202                         .append('svg')
88203                         .attr('class', 'icon')
88204                         .append('use')
88205                         .attr('xlink:href', '#fas-lock');
88206
88207                     container.call(_locked ? _lockedTip : _lockedTip.destroy);
88208             };
88209
88210
88211             field.state = function(val) {
88212                 if (!arguments.length) { return _state; }
88213                 _state = val;
88214                 return field;
88215             };
88216
88217
88218             field.tags = function(val) {
88219                 if (!arguments.length) { return _tags; }
88220                 _tags = val;
88221
88222                 if (tagsContainFieldKey() && !_show) {
88223                     // always show a field if it has a value to display
88224                     _show = true;
88225                     if (!field.impl) {
88226                         createField();
88227                     }
88228                 }
88229
88230                 return field;
88231             };
88232
88233
88234             field.locked = function(val) {
88235                 if (!arguments.length) { return _locked; }
88236                 _locked = val;
88237                 return field;
88238             };
88239
88240
88241             field.show = function() {
88242                 _show = true;
88243                 if (!field.impl) {
88244                     createField();
88245                 }
88246                 if (field.default && field.key && _tags[field.key] !== field.default) {
88247                     var t = {};
88248                     t[field.key] = field.default;
88249                     dispatch$1.call('change', this, t);
88250                 }
88251             };
88252
88253             // A shown field has a visible UI, a non-shown field is in the 'Add field' dropdown
88254             field.isShown = function() {
88255                 return _show;
88256             };
88257
88258
88259             // An allowed field can appear in the UI or in the 'Add field' dropdown.
88260             // A non-allowed field is hidden from the user altogether
88261             field.isAllowed = function() {
88262
88263                 if (entityIDs &&
88264                     entityIDs.length > 1 &&
88265                     uiFields[field.type].supportsMultiselection === false) { return false; }
88266
88267                 if (field.geometry && !entityIDs.every(function(entityID) {
88268                     return field.matchGeometry(context.graph().geometry(entityID));
88269                 })) { return false; }
88270
88271                 if (field.countryCodes || field.notCountryCodes) {
88272                     var extent = combinedEntityExtent();
88273                     if (!extent) { return true; }
88274
88275                     var center = extent.center();
88276                     var countryCode = iso1A2Code(center);
88277
88278                     if (!countryCode) { return false; }
88279
88280                     countryCode = countryCode.toLowerCase();
88281
88282                     if (field.countryCodes && field.countryCodes.indexOf(countryCode) === -1) {
88283                         return false;
88284                     }
88285                     if (field.notCountryCodes && field.notCountryCodes.indexOf(countryCode) !== -1) {
88286                         return false;
88287                     }
88288                 }
88289
88290                 var prerequisiteTag = field.prerequisiteTag;
88291
88292                 if (entityIDs &&
88293                     !tagsContainFieldKey() && // ignore tagging prerequisites if a value is already present
88294                     prerequisiteTag) {
88295
88296                     if (!entityIDs.every(function(entityID) {
88297                         var entity = context.graph().entity(entityID);
88298                         if (prerequisiteTag.key) {
88299                             var value = entity.tags[prerequisiteTag.key];
88300                             if (!value) { return false; }
88301
88302                             if (prerequisiteTag.valueNot) {
88303                                 return prerequisiteTag.valueNot !== value;
88304                             }
88305                             if (prerequisiteTag.value) {
88306                                 return prerequisiteTag.value === value;
88307                             }
88308                         } else if (prerequisiteTag.keyNot) {
88309                             if (entity.tags[prerequisiteTag.keyNot]) { return false; }
88310                         }
88311                         return true;
88312                     })) { return false; }
88313                 }
88314
88315                 return true;
88316             };
88317
88318
88319             field.focus = function() {
88320                 if (field.impl) {
88321                     field.impl.focus();
88322                 }
88323             };
88324
88325
88326             function combinedEntityExtent() {
88327                 return entityIDs && entityIDs.length && entityIDs.reduce(function(extent, entityID) {
88328                     var entity = context.graph().entity(entityID);
88329                     return extent.extend(entity.extent(context.graph()));
88330                 }, geoExtent());
88331             }
88332
88333
88334             return utilRebind(field, dispatch$1, 'on');
88335         }
88336
88337         function uiFormFields(context) {
88338             var moreCombo = uiCombobox(context, 'more-fields').minItems(1);
88339             var _fieldsArr = [];
88340             var _lastPlaceholder = '';
88341             var _state = '';
88342             var _klass = '';
88343
88344
88345             function formFields(selection) {
88346                 var allowedFields = _fieldsArr.filter(function(field) { return field.isAllowed(); });
88347                 var shown = allowedFields.filter(function(field) { return field.isShown(); });
88348                 var notShown = allowedFields.filter(function(field) { return !field.isShown(); });
88349
88350                 var container = selection.selectAll('.form-fields-container')
88351                     .data([0]);
88352
88353                 container = container.enter()
88354                     .append('div')
88355                     .attr('class', 'form-fields-container ' + (_klass || ''))
88356                     .merge(container);
88357
88358
88359                 var fields = container.selectAll('.wrap-form-field')
88360                     .data(shown, function(d) { return d.id + (d.entityIDs ? d.entityIDs.join() : ''); });
88361
88362                 fields.exit()
88363                     .remove();
88364
88365                 // Enter
88366                 var enter = fields.enter()
88367                     .append('div')
88368                     .attr('class', function(d) { return 'wrap-form-field wrap-form-field-' + d.safeid; });
88369
88370                 // Update
88371                 fields = fields
88372                     .merge(enter);
88373
88374                 fields
88375                     .order()
88376                     .each(function(d) {
88377                         select(this)
88378                             .call(d.render);
88379                     });
88380
88381
88382                 var titles = [];
88383                 var moreFields = notShown.map(function(field) {
88384                     var label = field.label();
88385                     titles.push(label);
88386
88387                     var terms = field.terms();
88388                     if (field.key) { terms.push(field.key); }
88389                     if (field.keys) { terms = terms.concat(field.keys); }
88390
88391                     return {
88392                         title: label,
88393                         value: label,
88394                         field: field,
88395                         terms: terms
88396                     };
88397                 });
88398
88399                 var placeholder = titles.slice(0,3).join(', ') + ((titles.length > 3) ? '…' : '');
88400
88401
88402                 var more = selection.selectAll('.more-fields')
88403                     .data((_state === 'hover' || moreFields.length === 0) ? [] : [0]);
88404
88405                 more.exit()
88406                     .remove();
88407
88408                 var moreEnter = more.enter()
88409                     .append('div')
88410                     .attr('class', 'more-fields')
88411                     .append('label');
88412
88413                 moreEnter
88414                     .append('span')
88415                     .text(_t('inspector.add_fields'));
88416
88417                 more = moreEnter
88418                     .merge(more);
88419
88420
88421                 var input = more.selectAll('.value')
88422                     .data([0]);
88423
88424                 input.exit()
88425                     .remove();
88426
88427                 input = input.enter()
88428                     .append('input')
88429                     .attr('class', 'value')
88430                     .attr('type', 'text')
88431                     .attr('placeholder', placeholder)
88432                     .call(utilNoAuto)
88433                     .merge(input);
88434
88435                 input
88436                     .call(utilGetSetValue, '')
88437                     .call(moreCombo
88438                         .data(moreFields)
88439                         .on('accept', function (d) {
88440                             if (!d) { return; }  // user entered something that was not matched
88441                             var field = d.field;
88442                             field.show();
88443                             selection.call(formFields);  // rerender
88444                             if (field.type !== 'semiCombo' && field.type !== 'multiCombo') {
88445                                 field.focus();
88446                             }
88447                         })
88448                     );
88449
88450                 // avoid updating placeholder excessively (triggers style recalc)
88451                 if (_lastPlaceholder !== placeholder) {
88452                     input.attr('placeholder', placeholder);
88453                     _lastPlaceholder = placeholder;
88454                 }
88455             }
88456
88457
88458             formFields.fieldsArr = function(val) {
88459                 if (!arguments.length) { return _fieldsArr; }
88460                 _fieldsArr = val || [];
88461                 return formFields;
88462             };
88463
88464             formFields.state = function(val) {
88465                 if (!arguments.length) { return _state; }
88466                 _state = val;
88467                 return formFields;
88468             };
88469
88470             formFields.klass = function(val) {
88471                 if (!arguments.length) { return _klass; }
88472                 _klass = val;
88473                 return formFields;
88474             };
88475
88476
88477             return formFields;
88478         }
88479
88480         function uiSectionPresetFields(context) {
88481
88482             var section = uiSection('preset-fields', context)
88483                 .title(function() {
88484                     return _t('inspector.fields');
88485                 })
88486                 .disclosureContent(renderDisclosureContent);
88487
88488             var dispatch$1 = dispatch('change', 'revert');
88489             var formFields = uiFormFields(context);
88490             var _state;
88491             var _fieldsArr;
88492             var _presets = [];
88493             var _tags;
88494             var _entityIDs;
88495
88496             function renderDisclosureContent(selection) {
88497                 if (!_fieldsArr) {
88498
88499                     var graph = context.graph();
88500
88501                     var geometries = Object.keys(_entityIDs.reduce(function(geoms, entityID) {
88502                         return geoms[graph.entity(entityID).geometry(graph)] = true;
88503                     }, {}));
88504
88505                     var presetsManager = _mainPresetIndex;
88506
88507                     var allFields = [];
88508                     var allMoreFields = [];
88509                     var sharedTotalFields;
88510
88511                     _presets.forEach(function(preset) {
88512                         var fields = preset.fields();
88513                         var moreFields = preset.moreFields();
88514
88515                         allFields = utilArrayUnion(allFields, fields);
88516                         allMoreFields = utilArrayUnion(allMoreFields, moreFields);
88517
88518                         if (!sharedTotalFields) {
88519                             sharedTotalFields = utilArrayUnion(fields, moreFields);
88520                         } else {
88521                             sharedTotalFields = sharedTotalFields.filter(function(field) {
88522                                 return fields.indexOf(field) !== -1 || moreFields.indexOf(field) !== -1;
88523                             });
88524                         }
88525                     });
88526
88527                     var sharedFields = allFields.filter(function(field) {
88528                         return sharedTotalFields.indexOf(field) !== -1;
88529                     });
88530                     var sharedMoreFields = allMoreFields.filter(function(field) {
88531                         return sharedTotalFields.indexOf(field) !== -1;
88532                     });
88533
88534                     _fieldsArr = [];
88535
88536                     sharedFields.forEach(function(field) {
88537                         if (field.matchAllGeometry(geometries)) {
88538                             _fieldsArr.push(
88539                                 uiField(context, field, _entityIDs)
88540                             );
88541                         }
88542                     });
88543
88544                     var singularEntity = _entityIDs.length === 1 && graph.hasEntity(_entityIDs[0]);
88545                     if (singularEntity && singularEntity.isHighwayIntersection(graph) && presetsManager.field('restrictions')) {
88546                         _fieldsArr.push(
88547                             uiField(context, presetsManager.field('restrictions'), _entityIDs)
88548                         );
88549                     }
88550
88551                     var additionalFields = utilArrayUnion(sharedMoreFields, presetsManager.universal());
88552                     additionalFields.sort(function(field1, field2) {
88553                         return field1.label().localeCompare(field2.label(), _mainLocalizer.localeCode());
88554                     });
88555
88556                     additionalFields.forEach(function(field) {
88557                         if (sharedFields.indexOf(field) === -1 &&
88558                             field.matchAllGeometry(geometries)) {
88559                             _fieldsArr.push(
88560                                 uiField(context, field, _entityIDs, { show: false })
88561                             );
88562                         }
88563                     });
88564
88565                     _fieldsArr.forEach(function(field) {
88566                         field
88567                             .on('change', function(t, onInput) {
88568                                 dispatch$1.call('change', field, _entityIDs, t, onInput);
88569                             })
88570                             .on('revert', function(keys) {
88571                                 dispatch$1.call('revert', field, keys);
88572                             });
88573                     });
88574                 }
88575
88576                 _fieldsArr.forEach(function(field) {
88577                     field
88578                         .state(_state)
88579                         .tags(_tags);
88580                 });
88581
88582
88583                 selection
88584                     .call(formFields
88585                         .fieldsArr(_fieldsArr)
88586                         .state(_state)
88587                         .klass('grouped-items-area')
88588                     );
88589
88590
88591                 selection.selectAll('.wrap-form-field input')
88592                     .on('keydown', function() {
88593                         // if user presses enter, and combobox is not active, accept edits..
88594                         if (event.keyCode === 13 && context.container().select('.combobox').empty()) {
88595                             context.enter(modeBrowse(context));
88596                         }
88597                     });
88598             }
88599
88600             section.presets = function(val) {
88601                 if (!arguments.length) { return _presets; }
88602                 if (!_presets || !val || !utilArrayIdentical(_presets, val)) {
88603                     _presets = val;
88604                     _fieldsArr = null;
88605                 }
88606                 return section;
88607             };
88608
88609             section.state = function(val) {
88610                 if (!arguments.length) { return _state; }
88611                 _state = val;
88612                 return section;
88613             };
88614
88615             section.tags = function(val) {
88616                 if (!arguments.length) { return _tags; }
88617                 _tags = val;
88618                 // Don't reset _fieldsArr here.
88619                 return section;
88620             };
88621
88622             section.entityIDs = function(val) {
88623                 if (!arguments.length) { return _entityIDs; }
88624                 if (!val || !_entityIDs || !utilArrayIdentical(_entityIDs, val)) {
88625                     _entityIDs = val;
88626                     _fieldsArr = null;
88627                 }
88628                 return section;
88629             };
88630
88631             return utilRebind(section, dispatch$1, 'on');
88632         }
88633
88634         function uiSectionRawMemberEditor(context) {
88635
88636             var section = uiSection('raw-member-editor', context)
88637                 .shouldDisplay(function() {
88638                     if (!_entityIDs || _entityIDs.length !== 1) { return false; }
88639
88640                     var entity = context.hasEntity(_entityIDs[0]);
88641                     return entity && entity.type === 'relation';
88642                 })
88643                 .title(function() {
88644                     var entity = context.hasEntity(_entityIDs[0]);
88645                     if (!entity) { return ''; }
88646
88647                     var gt = entity.members.length > _maxMembers ? '>' : '';
88648                     var count = gt + entity.members.slice(0, _maxMembers).length;
88649                     return _t('inspector.title_count', { title: _t('inspector.members'), count: count });
88650                 })
88651                 .disclosureContent(renderDisclosureContent);
88652
88653             var taginfo = services.taginfo;
88654             var _entityIDs;
88655             var _maxMembers = 1000;
88656
88657             function downloadMember(d) {
88658                 event.preventDefault();
88659
88660                 // display the loading indicator
88661                 select(this.parentNode).classed('tag-reference-loading', true);
88662                 context.loadEntity(d.id, function() {
88663                     section.reRender();
88664                 });
88665             }
88666
88667             function zoomToMember(d) {
88668                 event.preventDefault();
88669
88670                 var entity = context.entity(d.id);
88671                 context.map().zoomToEase(entity);
88672
88673                 // highlight the feature in case it wasn't previously on-screen
88674                 utilHighlightEntities([d.id], true, context);
88675             }
88676
88677
88678             function selectMember(d) {
88679                 event.preventDefault();
88680
88681                 // remove the hover-highlight styling
88682                 utilHighlightEntities([d.id], false, context);
88683
88684                 var entity = context.entity(d.id);
88685                 var mapExtent = context.map().extent();
88686                 if (!entity.intersects(mapExtent, context.graph())) {
88687                     // zoom to the entity if its extent is not visible now
88688                     context.map().zoomToEase(entity);
88689                 }
88690
88691                 context.enter(modeSelect(context, [d.id]));
88692             }
88693
88694
88695             function changeRole(d) {
88696                 var oldRole = d.role;
88697                 var newRole = context.cleanRelationRole(select(this).property('value'));
88698
88699                 if (oldRole !== newRole) {
88700                     var member = { id: d.id, type: d.type, role: newRole };
88701                     context.perform(
88702                         actionChangeMember(d.relation.id, member, d.index),
88703                         _t('operations.change_role.annotation')
88704                     );
88705                 }
88706             }
88707
88708
88709             function deleteMember(d) {
88710
88711                 // remove the hover-highlight styling
88712                 utilHighlightEntities([d.id], false, context);
88713
88714                 context.perform(
88715                     actionDeleteMember(d.relation.id, d.index),
88716                     _t('operations.delete_member.annotation')
88717                 );
88718
88719                 if (!context.hasEntity(d.relation.id)) {
88720                     context.enter(modeBrowse(context));
88721                 }
88722             }
88723
88724             function renderDisclosureContent(selection) {
88725
88726                 var entityID = _entityIDs[0];
88727
88728                 var memberships = [];
88729                 var entity = context.entity(entityID);
88730                 entity.members.slice(0, _maxMembers).forEach(function(member, index) {
88731                     memberships.push({
88732                         index: index,
88733                         id: member.id,
88734                         type: member.type,
88735                         role: member.role,
88736                         relation: entity,
88737                         member: context.hasEntity(member.id),
88738                         domId: utilUniqueDomId(entityID + '-member-' + index)
88739                     });
88740                 });
88741
88742                 var list = selection.selectAll('.member-list')
88743                     .data([0]);
88744
88745                 list = list.enter()
88746                     .append('ul')
88747                     .attr('class', 'member-list')
88748                     .merge(list);
88749
88750
88751                 var items = list.selectAll('li')
88752                     .data(memberships, function(d) {
88753                         return osmEntity.key(d.relation) + ',' + d.index + ',' +
88754                             (d.member ? osmEntity.key(d.member) : 'incomplete');
88755                     });
88756
88757                 items.exit()
88758                     .each(unbind)
88759                     .remove();
88760
88761                 var itemsEnter = items.enter()
88762                     .append('li')
88763                     .attr('class', 'member-row form-field')
88764                     .classed('member-incomplete', function(d) { return !d.member; });
88765
88766                 itemsEnter
88767                     .each(function(d) {
88768                         var item = select(this);
88769
88770                         var label = item
88771                             .append('label')
88772                             .attr('class', 'field-label')
88773                             .attr('for', d.domId);
88774
88775                         if (d.member) {
88776                             // highlight the member feature in the map while hovering on the list item
88777                             item
88778                                 .on('mouseover', function() {
88779                                     utilHighlightEntities([d.id], true, context);
88780                                 })
88781                                 .on('mouseout', function() {
88782                                     utilHighlightEntities([d.id], false, context);
88783                                 });
88784
88785                             var labelLink = label
88786                                 .append('span')
88787                                 .attr('class', 'label-text')
88788                                 .append('a')
88789                                 .attr('href', '#')
88790                                 .on('click', selectMember);
88791
88792                             labelLink
88793                                 .append('span')
88794                                 .attr('class', 'member-entity-type')
88795                                 .text(function(d) {
88796                                     var matched = _mainPresetIndex.match(d.member, context.graph());
88797                                     return (matched && matched.name()) || utilDisplayType(d.member.id);
88798                                 });
88799
88800                             labelLink
88801                                 .append('span')
88802                                 .attr('class', 'member-entity-name')
88803                                 .text(function(d) { return utilDisplayName(d.member); });
88804
88805                             label
88806                                 .append('button')
88807                                 .attr('tabindex', -1)
88808                                 .attr('title', _t('icons.remove'))
88809                                 .attr('class', 'remove member-delete')
88810                                 .call(svgIcon('#iD-operation-delete'));
88811
88812                             label
88813                                 .append('button')
88814                                 .attr('class', 'member-zoom')
88815                                 .attr('title', _t('icons.zoom_to'))
88816                                 .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
88817                                 .on('click', zoomToMember);
88818
88819                         } else {
88820                             var labelText = label
88821                                 .append('span')
88822                                 .attr('class', 'label-text');
88823
88824                             labelText
88825                                 .append('span')
88826                                 .attr('class', 'member-entity-type')
88827                                 .text(_t('inspector.' + d.type, { id: d.id }));
88828
88829                             labelText
88830                                 .append('span')
88831                                 .attr('class', 'member-entity-name')
88832                                 .text(_t('inspector.incomplete', { id: d.id }));
88833
88834                             label
88835                                 .append('button')
88836                                 .attr('class', 'member-download')
88837                                 .attr('title', _t('icons.download'))
88838                                 .attr('tabindex', -1)
88839                                 .call(svgIcon('#iD-icon-load'))
88840                                 .on('click', downloadMember);
88841                         }
88842                     });
88843
88844                 var wrapEnter = itemsEnter
88845                     .append('div')
88846                     .attr('class', 'form-field-input-wrap form-field-input-member');
88847
88848                 wrapEnter
88849                     .append('input')
88850                     .attr('class', 'member-role')
88851                     .attr('id', function(d) {
88852                         return d.domId;
88853                     })
88854                     .property('type', 'text')
88855                     .attr('placeholder', _t('inspector.role'))
88856                     .call(utilNoAuto);
88857
88858                 if (taginfo) {
88859                     wrapEnter.each(bindTypeahead);
88860                 }
88861
88862                 // update
88863                 items = items
88864                     .merge(itemsEnter)
88865                     .order();
88866
88867                 items.select('input.member-role')
88868                     .property('value', function(d) { return d.role; })
88869                     .on('blur', changeRole)
88870                     .on('change', changeRole);
88871
88872                 items.select('button.member-delete')
88873                     .on('click', deleteMember);
88874
88875                 var dragOrigin, targetIndex;
88876
88877                 items.call(d3_drag()
88878                     .on('start', function() {
88879                         dragOrigin = {
88880                             x: event.x,
88881                             y: event.y
88882                         };
88883                         targetIndex = null;
88884                     })
88885                     .on('drag', function(d, index) {
88886                         var x = event.x - dragOrigin.x,
88887                             y = event.y - dragOrigin.y;
88888
88889                         if (!select(this).classed('dragging') &&
88890                             // don't display drag until dragging beyond a distance threshold
88891                             Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) <= 5) { return; }
88892
88893                         select(this)
88894                             .classed('dragging', true);
88895
88896                         targetIndex = null;
88897
88898                         selection.selectAll('li.member-row')
88899                             .style('transform', function(d2, index2) {
88900                                 var node = select(this).node();
88901                                 if (index === index2) {
88902                                     return 'translate(' + x + 'px, ' + y + 'px)';
88903                                 } else if (index2 > index && event.y > node.offsetTop) {
88904                                     if (targetIndex === null || index2 > targetIndex) {
88905                                         targetIndex = index2;
88906                                     }
88907                                     return 'translateY(-100%)';
88908                                 } else if (index2 < index && event.y < node.offsetTop + node.offsetHeight) {
88909                                     if (targetIndex === null || index2 < targetIndex) {
88910                                         targetIndex = index2;
88911                                     }
88912                                     return 'translateY(100%)';
88913                                 }
88914                                 return null;
88915                             });
88916                     })
88917                     .on('end', function(d, index) {
88918
88919                         if (!select(this).classed('dragging')) {
88920                             return;
88921                         }
88922
88923                         select(this)
88924                             .classed('dragging', false);
88925
88926                         selection.selectAll('li.member-row')
88927                             .style('transform', null);
88928
88929                         if (targetIndex !== null) {
88930                             // dragged to a new position, reorder
88931                             context.perform(
88932                                 actionMoveMember(d.relation.id, index, targetIndex),
88933                                 _t('operations.reorder_members.annotation')
88934                             );
88935                         }
88936                     })
88937                 );
88938
88939
88940
88941                 function bindTypeahead(d) {
88942                     var row = select(this);
88943                     var role = row.selectAll('input.member-role');
88944                     var origValue = role.property('value');
88945
88946                     function sort(value, data) {
88947                         var sameletter = [];
88948                         var other = [];
88949                         for (var i = 0; i < data.length; i++) {
88950                             if (data[i].value.substring(0, value.length) === value) {
88951                                 sameletter.push(data[i]);
88952                             } else {
88953                                 other.push(data[i]);
88954                             }
88955                         }
88956                         return sameletter.concat(other);
88957                     }
88958
88959                     role.call(uiCombobox(context, 'member-role')
88960                         .fetcher(function(role, callback) {
88961                             // The `geometry` param is used in the `taginfo.js` interface for
88962                             // filtering results, as a key into the `tag_members_fractions`
88963                             // object.  If we don't know the geometry because the member is
88964                             // not yet downloaded, it's ok to guess based on type.
88965                             var geometry;
88966                             if (d.member) {
88967                                 geometry = context.graph().geometry(d.member.id);
88968                             } else if (d.type === 'relation') {
88969                                 geometry = 'relation';
88970                             } else if (d.type === 'way') {
88971                                 geometry = 'line';
88972                             } else {
88973                                 geometry = 'point';
88974                             }
88975
88976                             var rtype = entity.tags.type;
88977                             taginfo.roles({
88978                                 debounce: true,
88979                                 rtype: rtype || '',
88980                                 geometry: geometry,
88981                                 query: role
88982                             }, function(err, data) {
88983                                 if (!err) { callback(sort(role, data)); }
88984                             });
88985                         })
88986                         .on('cancel', function() {
88987                             role.property('value', origValue);
88988                         })
88989                     );
88990                 }
88991
88992
88993                 function unbind() {
88994                     var row = select(this);
88995
88996                     row.selectAll('input.member-role')
88997                         .call(uiCombobox.off, context);
88998                 }
88999             }
89000
89001             section.entityIDs = function(val) {
89002                 if (!arguments.length) { return _entityIDs; }
89003                 _entityIDs = val;
89004                 return section;
89005             };
89006
89007
89008             return section;
89009         }
89010
89011         function uiSectionRawMembershipEditor(context) {
89012
89013             var section = uiSection('raw-membership-editor', context)
89014                 .shouldDisplay(function() {
89015                     return _entityIDs && _entityIDs.length === 1;
89016                 })
89017                 .title(function() {
89018                     var entity = context.hasEntity(_entityIDs[0]);
89019                     if (!entity) { return ''; }
89020
89021                     var parents = context.graph().parentRelations(entity);
89022                     var gt = parents.length > _maxMemberships ? '>' : '';
89023                     var count = gt + parents.slice(0, _maxMemberships).length;
89024                     return _t('inspector.title_count', { title: _t('inspector.relations'), count: count });
89025                 })
89026                 .disclosureContent(renderDisclosureContent);
89027
89028             var taginfo = services.taginfo;
89029             var nearbyCombo = uiCombobox(context, 'parent-relation')
89030                 .minItems(1)
89031                 .fetcher(fetchNearbyRelations)
89032                 .itemsMouseEnter(function(d) {
89033                     if (d.relation) { utilHighlightEntities([d.relation.id], true, context); }
89034                 })
89035                 .itemsMouseLeave(function(d) {
89036                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89037                 });
89038             var _inChange = false;
89039             var _entityIDs = [];
89040             var _showBlank;
89041             var _maxMemberships = 1000;
89042
89043             function selectRelation(d) {
89044                 event.preventDefault();
89045
89046                 // remove the hover-highlight styling
89047                 utilHighlightEntities([d.relation.id], false, context);
89048
89049                 context.enter(modeSelect(context, [d.relation.id]));
89050             }
89051
89052             function zoomToRelation(d) {
89053                 event.preventDefault();
89054
89055                 var entity = context.entity(d.relation.id);
89056                 context.map().zoomToEase(entity);
89057
89058                 // highlight the relation in case it wasn't previously on-screen
89059                 utilHighlightEntities([d.relation.id], true, context);
89060             }
89061
89062
89063             function changeRole(d) {
89064                 if (d === 0) { return; }    // called on newrow (shoudn't happen)
89065                 if (_inChange) { return; }  // avoid accidental recursive call #5731
89066
89067                 var oldRole = d.member.role;
89068                 var newRole = context.cleanRelationRole(select(this).property('value'));
89069
89070                 if (oldRole !== newRole) {
89071                     _inChange = true;
89072                     context.perform(
89073                         actionChangeMember(d.relation.id, Object.assign({}, d.member, { role: newRole }), d.index),
89074                         _t('operations.change_role.annotation')
89075                     );
89076                 }
89077                 _inChange = false;
89078             }
89079
89080
89081             function addMembership(d, role) {
89082                 this.blur();           // avoid keeping focus on the button
89083                 _showBlank = false;
89084
89085                 var member = { id: _entityIDs[0], type: context.entity(_entityIDs[0]).type, role: role };
89086
89087                 if (d.relation) {
89088                     context.perform(
89089                         actionAddMember(d.relation.id, member),
89090                         _t('operations.add_member.annotation')
89091                     );
89092
89093                 } else {
89094                     var relation = osmRelation();
89095                     context.perform(
89096                         actionAddEntity(relation),
89097                         actionAddMember(relation.id, member),
89098                         _t('operations.add.annotation.relation')
89099                     );
89100
89101                     context.enter(modeSelect(context, [relation.id]).newFeature(true));
89102                 }
89103             }
89104
89105
89106             function deleteMembership(d) {
89107                 this.blur();           // avoid keeping focus on the button
89108                 if (d === 0) { return; }   // called on newrow (shoudn't happen)
89109
89110                 // remove the hover-highlight styling
89111                 utilHighlightEntities([d.relation.id], false, context);
89112
89113                 context.perform(
89114                     actionDeleteMember(d.relation.id, d.index),
89115                     _t('operations.delete_member.annotation')
89116                 );
89117             }
89118
89119
89120             function fetchNearbyRelations(q, callback) {
89121                 var newRelation = { relation: null, value: _t('inspector.new_relation') };
89122
89123                 var entityID = _entityIDs[0];
89124
89125                 var result = [];
89126
89127                 var graph = context.graph();
89128
89129                 function baseDisplayLabel(entity) {
89130                     var matched = _mainPresetIndex.match(entity, graph);
89131                     var presetName = (matched && matched.name()) || _t('inspector.relation');
89132                     var entityName = utilDisplayName(entity) || '';
89133
89134                     return presetName + ' ' + entityName;
89135                 }
89136
89137                 var explicitRelation = q && context.hasEntity(q.toLowerCase());
89138                 if (explicitRelation && explicitRelation.type === 'relation' && explicitRelation.id !== entityID) {
89139                     // loaded relation is specified explicitly, only show that
89140
89141                     result.push({
89142                         relation: explicitRelation,
89143                         value: baseDisplayLabel(explicitRelation) + ' ' + explicitRelation.id
89144                     });
89145                 } else {
89146
89147                     context.history().intersects(context.map().extent()).forEach(function(entity) {
89148                         if (entity.type !== 'relation' || entity.id === entityID) { return; }
89149
89150                         var value = baseDisplayLabel(entity);
89151                         if (q && (value + ' ' + entity.id).toLowerCase().indexOf(q.toLowerCase()) === -1) { return; }
89152
89153                         result.push({ relation: entity, value: value });
89154                     });
89155
89156                     result.sort(function(a, b) {
89157                         return osmRelation.creationOrder(a.relation, b.relation);
89158                     });
89159
89160                     // Dedupe identical names by appending relation id - see #2891
89161                     var dupeGroups = Object.values(utilArrayGroupBy(result, 'value'))
89162                         .filter(function(v) { return v.length > 1; });
89163
89164                     dupeGroups.forEach(function(group) {
89165                         group.forEach(function(obj) {
89166                             obj.value += ' ' + obj.relation.id;
89167                         });
89168                     });
89169                 }
89170
89171                 result.forEach(function(obj) {
89172                     obj.title = obj.value;
89173                 });
89174
89175                 result.unshift(newRelation);
89176                 callback(result);
89177             }
89178
89179             function renderDisclosureContent(selection) {
89180
89181                 var entityID = _entityIDs[0];
89182
89183                 var entity = context.entity(entityID);
89184                 var parents = context.graph().parentRelations(entity);
89185
89186                 var memberships = [];
89187
89188                 parents.slice(0, _maxMemberships).forEach(function(relation) {
89189                     relation.members.forEach(function(member, index) {
89190                         if (member.id === entity.id) {
89191                             memberships.push({
89192                                 relation: relation,
89193                                 member: member,
89194                                 index: index,
89195                                 domId: utilUniqueDomId(entityID + '-membership-' + relation.id + '-' + index)
89196                             });
89197                         }
89198                     });
89199                 });
89200
89201                 var list = selection.selectAll('.member-list')
89202                     .data([0]);
89203
89204                 list = list.enter()
89205                     .append('ul')
89206                     .attr('class', 'member-list')
89207                     .merge(list);
89208
89209
89210                 var items = list.selectAll('li.member-row-normal')
89211                     .data(memberships, function(d) {
89212                         return osmEntity.key(d.relation) + ',' + d.index;
89213                     });
89214
89215                 items.exit()
89216                     .each(unbind)
89217                     .remove();
89218
89219                 // Enter
89220                 var itemsEnter = items.enter()
89221                     .append('li')
89222                     .attr('class', 'member-row member-row-normal form-field');
89223
89224                 // highlight the relation in the map while hovering on the list item
89225                 itemsEnter.on('mouseover', function(d) {
89226                         utilHighlightEntities([d.relation.id], true, context);
89227                     })
89228                     .on('mouseout', function(d) {
89229                         utilHighlightEntities([d.relation.id], false, context);
89230                     });
89231
89232                 var labelEnter = itemsEnter
89233                     .append('label')
89234                     .attr('class', 'field-label')
89235                     .attr('for', function(d) {
89236                         return d.domId;
89237                     });
89238
89239                 var labelLink = labelEnter
89240                     .append('span')
89241                     .attr('class', 'label-text')
89242                     .append('a')
89243                     .attr('href', '#')
89244                     .on('click', selectRelation);
89245
89246                 labelLink
89247                     .append('span')
89248                     .attr('class', 'member-entity-type')
89249                     .text(function(d) {
89250                         var matched = _mainPresetIndex.match(d.relation, context.graph());
89251                         return (matched && matched.name()) || _t('inspector.relation');
89252                     });
89253
89254                 labelLink
89255                     .append('span')
89256                     .attr('class', 'member-entity-name')
89257                     .text(function(d) { return utilDisplayName(d.relation); });
89258
89259                 labelEnter
89260                     .append('button')
89261                     .attr('tabindex', -1)
89262                     .attr('class', 'remove member-delete')
89263                     .call(svgIcon('#iD-operation-delete'))
89264                     .on('click', deleteMembership);
89265
89266                 labelEnter
89267                     .append('button')
89268                     .attr('class', 'member-zoom')
89269                     .attr('title', _t('icons.zoom_to'))
89270                     .call(svgIcon('#iD-icon-framed-dot', 'monochrome'))
89271                     .on('click', zoomToRelation);
89272
89273                 var wrapEnter = itemsEnter
89274                     .append('div')
89275                     .attr('class', 'form-field-input-wrap form-field-input-member');
89276
89277                 wrapEnter
89278                     .append('input')
89279                     .attr('class', 'member-role')
89280                     .attr('id', function(d) {
89281                         return d.domId;
89282                     })
89283                     .property('type', 'text')
89284                     .attr('placeholder', _t('inspector.role'))
89285                     .call(utilNoAuto)
89286                     .property('value', function(d) { return d.member.role; })
89287                     .on('blur', changeRole)
89288                     .on('change', changeRole);
89289
89290                 if (taginfo) {
89291                     wrapEnter.each(bindTypeahead);
89292                 }
89293
89294
89295                 var newMembership = list.selectAll('.member-row-new')
89296                     .data(_showBlank ? [0] : []);
89297
89298                 // Exit
89299                 newMembership.exit()
89300                     .remove();
89301
89302                 // Enter
89303                 var newMembershipEnter = newMembership.enter()
89304                     .append('li')
89305                     .attr('class', 'member-row member-row-new form-field');
89306
89307                 var newLabelEnter = newMembershipEnter
89308                     .append('label')
89309                     .attr('class', 'field-label');
89310
89311                 newLabelEnter
89312                     .append('input')
89313                     .attr('placeholder', _t('inspector.choose_relation'))
89314                     .attr('type', 'text')
89315                     .attr('class', 'member-entity-input')
89316                     .call(utilNoAuto);
89317
89318                 newLabelEnter
89319                     .append('button')
89320                     .attr('tabindex', -1)
89321                     .attr('class', 'remove member-delete')
89322                     .call(svgIcon('#iD-operation-delete'))
89323                     .on('click', function() {
89324                         list.selectAll('.member-row-new')
89325                             .remove();
89326                     });
89327
89328                 var newWrapEnter = newMembershipEnter
89329                     .append('div')
89330                     .attr('class', 'form-field-input-wrap form-field-input-member');
89331
89332                 newWrapEnter
89333                     .append('input')
89334                     .attr('class', 'member-role')
89335                     .property('type', 'text')
89336                     .attr('placeholder', _t('inspector.role'))
89337                     .call(utilNoAuto);
89338
89339                 // Update
89340                 newMembership = newMembership
89341                     .merge(newMembershipEnter);
89342
89343                 newMembership.selectAll('.member-entity-input')
89344                     .on('blur', cancelEntity)   // if it wasn't accepted normally, cancel it
89345                     .call(nearbyCombo
89346                         .on('accept', acceptEntity)
89347                         .on('cancel', cancelEntity)
89348                     );
89349
89350
89351                 // Container for the Add button
89352                 var addRow = selection.selectAll('.add-row')
89353                     .data([0]);
89354
89355                 // enter
89356                 var addRowEnter = addRow.enter()
89357                     .append('div')
89358                     .attr('class', 'add-row');
89359
89360                 var addRelationButton = addRowEnter
89361                     .append('button')
89362                     .attr('class', 'add-relation');
89363
89364                 addRelationButton
89365                     .call(svgIcon('#iD-icon-plus', 'light'));
89366                 addRelationButton
89367                     .call(uiTooltip().title(_t('inspector.add_to_relation')).placement(_mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left'));
89368
89369                 addRowEnter
89370                     .append('div')
89371                     .attr('class', 'space-value');   // preserve space
89372
89373                 addRowEnter
89374                     .append('div')
89375                     .attr('class', 'space-buttons');  // preserve space
89376
89377                 // update
89378                 addRow = addRow
89379                     .merge(addRowEnter);
89380
89381                 addRow.select('.add-relation')
89382                     .on('click', function() {
89383                         _showBlank = true;
89384                         section.reRender();
89385                         list.selectAll('.member-entity-input').node().focus();
89386                     });
89387
89388
89389                 function acceptEntity(d) {
89390                     if (!d) {
89391                         cancelEntity();
89392                         return;
89393                     }
89394                     // remove hover-higlighting
89395                     if (d.relation) { utilHighlightEntities([d.relation.id], false, context); }
89396
89397                     var role = context.cleanRelationRole(list.selectAll('.member-row-new .member-role').property('value'));
89398                     addMembership(d, role);
89399                 }
89400
89401
89402                 function cancelEntity() {
89403                     var input = newMembership.selectAll('.member-entity-input');
89404                     input.property('value', '');
89405
89406                     // remove hover-higlighting
89407                     context.surface().selectAll('.highlighted')
89408                         .classed('highlighted', false);
89409                 }
89410
89411
89412                 function bindTypeahead(d) {
89413                     var row = select(this);
89414                     var role = row.selectAll('input.member-role');
89415                     var origValue = role.property('value');
89416
89417                     function sort(value, data) {
89418                         var sameletter = [];
89419                         var other = [];
89420                         for (var i = 0; i < data.length; i++) {
89421                             if (data[i].value.substring(0, value.length) === value) {
89422                                 sameletter.push(data[i]);
89423                             } else {
89424                                 other.push(data[i]);
89425                             }
89426                         }
89427                         return sameletter.concat(other);
89428                     }
89429
89430                     role.call(uiCombobox(context, 'member-role')
89431                         .fetcher(function(role, callback) {
89432                             var rtype = d.relation.tags.type;
89433                             taginfo.roles({
89434                                 debounce: true,
89435                                 rtype: rtype || '',
89436                                 geometry: context.graph().geometry(entityID),
89437                                 query: role
89438                             }, function(err, data) {
89439                                 if (!err) { callback(sort(role, data)); }
89440                             });
89441                         })
89442                         .on('cancel', function() {
89443                             role.property('value', origValue);
89444                         })
89445                     );
89446                 }
89447
89448
89449                 function unbind() {
89450                     var row = select(this);
89451
89452                     row.selectAll('input.member-role')
89453                         .call(uiCombobox.off, context);
89454                 }
89455             }
89456
89457
89458             section.entityIDs = function(val) {
89459                 if (!arguments.length) { return _entityIDs; }
89460                 _entityIDs = val;
89461                 _showBlank = false;
89462                 return section;
89463             };
89464
89465
89466             return section;
89467         }
89468
89469         function uiSectionSelectionList(context) {
89470
89471             var _selectedIDs = [];
89472
89473             var section = uiSection('selected-features', context)
89474                 .shouldDisplay(function() {
89475                     return _selectedIDs.length > 1;
89476                 })
89477                 .title(function() {
89478                     return _t('inspector.title_count', { title: _t('inspector.features'), count: _selectedIDs.length });
89479                 })
89480                 .disclosureContent(renderDisclosureContent);
89481
89482             context.history()
89483                 .on('change.selectionList', function(difference) {
89484                     if (difference) {
89485                         section.reRender();
89486                     }
89487                 });
89488
89489             section.entityIDs = function(val) {
89490                 if (!arguments.length) { return _selectedIDs; }
89491                 _selectedIDs = val;
89492                 return section;
89493             };
89494
89495             function selectEntity(entity) {
89496                 context.enter(modeSelect(context, [entity.id]));
89497             }
89498
89499             function deselectEntity(entity) {
89500                 event.stopPropagation();
89501
89502                 var selectedIDs = _selectedIDs.slice();
89503                 var index = selectedIDs.indexOf(entity.id);
89504                 if (index > -1) {
89505                     selectedIDs.splice(index, 1);
89506                     context.enter(modeSelect(context, selectedIDs));
89507                 }
89508             }
89509
89510             function renderDisclosureContent(selection) {
89511
89512                 var list = selection.selectAll('.feature-list')
89513                     .data([0]);
89514
89515                 list = list.enter()
89516                     .append('div')
89517                     .attr('class', 'feature-list')
89518                     .merge(list);
89519
89520                 var entities = _selectedIDs
89521                     .map(function(id) { return context.hasEntity(id); })
89522                     .filter(Boolean);
89523
89524                 var items = list.selectAll('.feature-list-item')
89525                     .data(entities, osmEntity.key);
89526
89527                 items.exit()
89528                     .remove();
89529
89530                 // Enter
89531                 var enter = items.enter()
89532                     .append('div')
89533                     .attr('class', 'feature-list-item')
89534                     .on('click', selectEntity);
89535
89536                 enter
89537                     .each(function(d) {
89538                         select(this).on('mouseover', function() {
89539                             utilHighlightEntities([d.id], true, context);
89540                         });
89541                         select(this).on('mouseout', function() {
89542                             utilHighlightEntities([d.id], false, context);
89543                         });
89544                     });
89545
89546                 var label = enter
89547                     .append('button')
89548                     .attr('class', 'label');
89549
89550                 enter
89551                     .append('button')
89552                     .attr('class', 'close')
89553                     .attr('title', _t('icons.deselect'))
89554                     .on('click', deselectEntity)
89555                     .call(svgIcon('#iD-icon-close'));
89556
89557                 label
89558                     .append('span')
89559                     .attr('class', 'entity-geom-icon')
89560                     .call(svgIcon('', 'pre-text'));
89561
89562                 label
89563                     .append('span')
89564                     .attr('class', 'entity-type');
89565
89566                 label
89567                     .append('span')
89568                     .attr('class', 'entity-name');
89569
89570                 // Update
89571                 items = items.merge(enter);
89572
89573                 items.selectAll('.entity-geom-icon use')
89574                     .attr('href', function() {
89575                         var entity = this.parentNode.parentNode.__data__;
89576                         return '#iD-icon-' + entity.geometry(context.graph());
89577                     });
89578
89579                 items.selectAll('.entity-type')
89580                     .text(function(entity) { return _mainPresetIndex.match(entity, context.graph()).name(); });
89581
89582                 items.selectAll('.entity-name')
89583                     .text(function(d) {
89584                         // fetch latest entity
89585                         var entity = context.entity(d.id);
89586                         return utilDisplayName(entity);
89587                     });
89588             }
89589
89590             return section;
89591         }
89592
89593         function uiEntityEditor(context) {
89594             var dispatch$1 = dispatch('choose');
89595             var _state = 'select';
89596             var _coalesceChanges = false;
89597             var _modified = false;
89598             var _base;
89599             var _entityIDs;
89600             var _activePresets = [];
89601             var _newFeature;
89602
89603             var _sections;
89604
89605             function entityEditor(selection) {
89606
89607                 var combinedTags = utilCombinedTags(_entityIDs, context.graph());
89608
89609                 // Header
89610                 var header = selection.selectAll('.header')
89611                     .data([0]);
89612
89613                 // Enter
89614                 var headerEnter = header.enter()
89615                     .append('div')
89616                     .attr('class', 'header fillL cf');
89617
89618                 headerEnter
89619                     .append('button')
89620                     .attr('class', 'preset-reset preset-choose')
89621                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-forward' : '#iD-icon-backward'));
89622
89623                 headerEnter
89624                     .append('button')
89625                     .attr('class', 'close')
89626                     .on('click', function() { context.enter(modeBrowse(context)); })
89627                     .call(svgIcon(_modified ? '#iD-icon-apply' : '#iD-icon-close'));
89628
89629                 headerEnter
89630                     .append('h3');
89631
89632                 // Update
89633                 header = header
89634                     .merge(headerEnter);
89635
89636                 header.selectAll('h3')
89637                     .text(_entityIDs.length === 1 ? _t('inspector.edit') : _t('inspector.edit_features'));
89638
89639                 header.selectAll('.preset-reset')
89640                     .on('click', function() {
89641                         dispatch$1.call('choose', this, _activePresets);
89642                     });
89643
89644                 // Body
89645                 var body = selection.selectAll('.inspector-body')
89646                     .data([0]);
89647
89648                 // Enter
89649                 var bodyEnter = body.enter()
89650                     .append('div')
89651                     .attr('class', 'entity-editor inspector-body sep-top');
89652
89653                 // Update
89654                 body = body
89655                     .merge(bodyEnter);
89656
89657                 if (!_sections) {
89658                     _sections = [
89659                         uiSectionSelectionList(context),
89660                         uiSectionFeatureType(context).on('choose', function(presets) {
89661                             dispatch$1.call('choose', this, presets);
89662                         }),
89663                         uiSectionEntityIssues(context),
89664                         uiSectionPresetFields(context).on('change', changeTags).on('revert', revertTags),
89665                         uiSectionRawTagEditor('raw-tag-editor', context).on('change', changeTags),
89666                         uiSectionRawMemberEditor(context),
89667                         uiSectionRawMembershipEditor(context)
89668                     ];
89669                 }
89670
89671                 _sections.forEach(function(section) {
89672                     if (section.entityIDs) {
89673                         section.entityIDs(_entityIDs);
89674                     }
89675                     if (section.presets) {
89676                         section.presets(_activePresets);
89677                     }
89678                     if (section.tags) {
89679                         section.tags(combinedTags);
89680                     }
89681                     if (section.state) {
89682                         section.state(_state);
89683                     }
89684                     body.call(section.render);
89685                 });
89686
89687                 body
89688                     .selectAll('.key-trap-wrap')
89689                     .data([0])
89690                     .enter()
89691                     .append('div')
89692                     .attr('class', 'key-trap-wrap')
89693                     .append('input')
89694                     .attr('type', 'text')
89695                     .attr('class', 'key-trap')
89696                     .on('keydown.key-trap', function() {
89697                         // On tabbing, send focus back to the first field on the inspector-body
89698                         // (probably the `name` field) #4159
89699                         if (event.keyCode === 9 && !event.shiftKey) {
89700                             event.preventDefault();
89701                             body.select('input').node().focus();
89702                         }
89703                     });
89704
89705                 context.history()
89706                     .on('change.entity-editor', historyChanged);
89707
89708                 function historyChanged(difference) {
89709                     if (selection.selectAll('.entity-editor').empty()) { return; }
89710                     if (_state === 'hide') { return; }
89711                     var significant = !difference ||
89712                             difference.didChange.properties ||
89713                             difference.didChange.addition ||
89714                             difference.didChange.deletion;
89715                     if (!significant) { return; }
89716
89717                     _entityIDs = _entityIDs.filter(context.hasEntity);
89718                     if (!_entityIDs.length) { return; }
89719
89720                     var priorActivePreset = _activePresets.length === 1 && _activePresets[0];
89721
89722                     loadActivePresets();
89723
89724                     var graph = context.graph();
89725                     entityEditor.modified(_base !== graph);
89726                     entityEditor(selection);
89727
89728                     if (priorActivePreset && _activePresets.length === 1 && priorActivePreset !== _activePresets[0]) {
89729                         // flash the button to indicate the preset changed
89730                         context.container().selectAll('.entity-editor button.preset-reset .label')
89731                             .style('background-color', '#fff')
89732                             .transition()
89733                             .duration(750)
89734                             .style('background-color', null);
89735                     }
89736                 }
89737             }
89738
89739
89740             // Tag changes that fire on input can all get coalesced into a single
89741             // history operation when the user leaves the field.  #2342
89742             // Use explicit entityIDs in case the selection changes before the event is fired.
89743             function changeTags(entityIDs, changed, onInput) {
89744
89745                 var actions = [];
89746                 for (var i in entityIDs) {
89747                     var entityID = entityIDs[i];
89748                     var entity = context.entity(entityID);
89749
89750                     var tags = Object.assign({}, entity.tags);   // shallow copy
89751
89752                     for (var k in changed) {
89753                         if (!k) { continue; }
89754                         var v = changed[k];
89755                         if (v !== undefined || tags.hasOwnProperty(k)) {
89756                             tags[k] = v;
89757                         }
89758                     }
89759
89760                     if (!onInput) {
89761                         tags = utilCleanTags(tags);
89762                     }
89763
89764                     if (!fastDeepEqual(entity.tags, tags)) {
89765                         actions.push(actionChangeTags(entityID, tags));
89766                     }
89767                 }
89768
89769                 if (actions.length) {
89770                     var combinedAction = function(graph) {
89771                         actions.forEach(function(action) {
89772                             graph = action(graph);
89773                         });
89774                         return graph;
89775                     };
89776
89777                     var annotation = _t('operations.change_tags.annotation');
89778
89779                     if (_coalesceChanges) {
89780                         context.overwrite(combinedAction, annotation);
89781                     } else {
89782                         context.perform(combinedAction, annotation);
89783                         _coalesceChanges = !!onInput;
89784                     }
89785                 }
89786
89787                 // if leaving field (blur event), rerun validation
89788                 if (!onInput) {
89789                     context.validator().validate();
89790                 }
89791             }
89792
89793             function revertTags(keys) {
89794
89795                 var actions = [];
89796                 for (var i in _entityIDs) {
89797                     var entityID = _entityIDs[i];
89798
89799                     var original = context.graph().base().entities[entityID];
89800                     var changed = {};
89801                     for (var j in keys) {
89802                         var key = keys[j];
89803                         changed[key] = original ? original.tags[key] : undefined;
89804                     }
89805
89806                     var entity = context.entity(entityID);
89807                     var tags = Object.assign({}, entity.tags);   // shallow copy
89808
89809                     for (var k in changed) {
89810                         if (!k) { continue; }
89811                         var v = changed[k];
89812                         if (v !== undefined || tags.hasOwnProperty(k)) {
89813                             tags[k] = v;
89814                         }
89815                     }
89816
89817
89818                     tags = utilCleanTags(tags);
89819
89820                     if (!fastDeepEqual(entity.tags, tags)) {
89821                         actions.push(actionChangeTags(entityID, tags));
89822                     }
89823
89824                 }
89825
89826                 if (actions.length) {
89827                     var combinedAction = function(graph) {
89828                         actions.forEach(function(action) {
89829                             graph = action(graph);
89830                         });
89831                         return graph;
89832                     };
89833
89834                     var annotation = _t('operations.change_tags.annotation');
89835
89836                     if (_coalesceChanges) {
89837                         context.overwrite(combinedAction, annotation);
89838                     } else {
89839                         context.perform(combinedAction, annotation);
89840                         _coalesceChanges = false;
89841                     }
89842                 }
89843
89844                 context.validator().validate();
89845             }
89846
89847
89848             entityEditor.modified = function(val) {
89849                 if (!arguments.length) { return _modified; }
89850                 _modified = val;
89851                 return entityEditor;
89852             };
89853
89854
89855             entityEditor.state = function(val) {
89856                 if (!arguments.length) { return _state; }
89857                 _state = val;
89858                 return entityEditor;
89859             };
89860
89861
89862             entityEditor.entityIDs = function(val) {
89863                 if (!arguments.length) { return _entityIDs; }
89864                 if (val && _entityIDs && utilArrayIdentical(_entityIDs, val)) { return entityEditor; }  // exit early if no change
89865
89866                 _entityIDs = val;
89867                 _base = context.graph();
89868                 _coalesceChanges = false;
89869
89870                 loadActivePresets();
89871
89872                 return entityEditor
89873                     .modified(false);
89874             };
89875
89876
89877             entityEditor.newFeature = function(val) {
89878                 if (!arguments.length) { return _newFeature; }
89879                 _newFeature = val;
89880                 return entityEditor;
89881             };
89882
89883
89884             function loadActivePresets() {
89885
89886                 var graph = context.graph();
89887
89888                 var counts = {};
89889
89890                 for (var i in _entityIDs) {
89891                     var entity = graph.hasEntity(_entityIDs[i]);
89892                     if (!entity) { return; }
89893
89894                     var match = _mainPresetIndex.match(entity, graph);
89895
89896                     if (!counts[match.id]) { counts[match.id] = 0; }
89897                     counts[match.id] += 1;
89898                 }
89899
89900                 var matches = Object.keys(counts).sort(function(p1, p2) {
89901                     return counts[p2] - counts[p1];
89902                 }).map(function(pID) {
89903                     return _mainPresetIndex.item(pID);
89904                 });
89905
89906                 // A "weak" preset doesn't set any tags. (e.g. "Address")
89907                 var weakPreset = _activePresets.length === 1 &&
89908                     Object.keys(_activePresets[0].addTags || {}).length === 0;
89909                 // Don't replace a weak preset with a fallback preset (e.g. "Point")
89910                 if (weakPreset && matches.length === 1 && matches[0].isFallback()) { return; }
89911
89912                 entityEditor.presets(matches);
89913             }
89914
89915             entityEditor.presets = function(val) {
89916                 if (!arguments.length) { return _activePresets; }
89917
89918                 // don't reload the same preset
89919                 if (!utilArrayIdentical(val, _activePresets)) {
89920                     _activePresets = val;
89921                 }
89922                 return entityEditor;
89923             };
89924
89925             return utilRebind(entityEditor, dispatch$1, 'on');
89926         }
89927
89928         function uiPresetList(context) {
89929             var dispatch$1 = dispatch('cancel', 'choose');
89930             var _entityIDs;
89931             var _currentPresets;
89932             var _autofocus = false;
89933
89934
89935             function presetList(selection) {
89936                 if (!_entityIDs) { return; }
89937
89938                 var presets = _mainPresetIndex.matchAllGeometry(entityGeometries());
89939
89940                 selection.html('');
89941
89942                 var messagewrap = selection
89943                     .append('div')
89944                     .attr('class', 'header fillL');
89945
89946                 var message = messagewrap
89947                     .append('h3')
89948                     .text(_t('inspector.choose'));
89949
89950                 messagewrap
89951                     .append('button')
89952                     .attr('class', 'preset-choose')
89953                     .on('click', function() { dispatch$1.call('cancel', this); })
89954                     .call(svgIcon((_mainLocalizer.textDirection() === 'rtl') ? '#iD-icon-backward' : '#iD-icon-forward'));
89955
89956                 function initialKeydown() {
89957                     // hack to let delete shortcut work when search is autofocused
89958                     if (search.property('value').length === 0 &&
89959                         (event.keyCode === utilKeybinding.keyCodes['⌫'] ||
89960                          event.keyCode === utilKeybinding.keyCodes['⌦'])) {
89961                         event.preventDefault();
89962                         event.stopPropagation();
89963                         operationDelete(context, _entityIDs)();
89964
89965                     // hack to let undo work when search is autofocused
89966                     } else if (search.property('value').length === 0 &&
89967                         (event.ctrlKey || event.metaKey) &&
89968                         event.keyCode === utilKeybinding.keyCodes.z) {
89969                         event.preventDefault();
89970                         event.stopPropagation();
89971                         context.undo();
89972                     } else if (!event.ctrlKey && !event.metaKey) {
89973                         // don't check for delete/undo hack on future keydown events
89974                         select(this).on('keydown', keydown);
89975                         keydown.call(this);
89976                     }
89977                 }
89978
89979                 function keydown() {
89980                     // down arrow
89981                     if (event.keyCode === utilKeybinding.keyCodes['↓'] &&
89982                         // if insertion point is at the end of the string
89983                         search.node().selectionStart === search.property('value').length) {
89984                         event.preventDefault();
89985                         event.stopPropagation();
89986                         // move focus to the first item in the preset list
89987                         var buttons = list.selectAll('.preset-list-button');
89988                         if (!buttons.empty()) { buttons.nodes()[0].focus(); }
89989                     }
89990                 }
89991
89992                 function keypress() {
89993                     // enter
89994                     var value = search.property('value');
89995                     if (event.keyCode === 13 && value.length) {
89996                         list.selectAll('.preset-list-item:first-child')
89997                             .each(function(d) { d.choose.call(this); });
89998                     }
89999                 }
90000
90001                 function inputevent() {
90002                     var value = search.property('value');
90003                     list.classed('filtered', value.length);
90004                     var extent = combinedEntityExtent();
90005                     var results, messageText;
90006                     if (value.length && extent) {
90007                         var center = extent.center();
90008                         var countryCode = iso1A2Code(center);
90009
90010                         results = presets.search(value, entityGeometries()[0], countryCode && countryCode.toLowerCase());
90011                         messageText = _t('inspector.results', {
90012                             n: results.collection.length,
90013                             search: value
90014                         });
90015                     } else {
90016                         results = _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro());
90017                         messageText = _t('inspector.choose');
90018                     }
90019                     list.call(drawList, results);
90020                     message.text(messageText);
90021                 }
90022
90023                 var searchWrap = selection
90024                     .append('div')
90025                     .attr('class', 'search-header');
90026
90027                 var search = searchWrap
90028                     .append('input')
90029                     .attr('class', 'preset-search-input')
90030                     .attr('placeholder', _t('inspector.search'))
90031                     .attr('type', 'search')
90032                     .call(utilNoAuto)
90033                     .on('keydown', initialKeydown)
90034                     .on('keypress', keypress)
90035                     .on('input', inputevent);
90036
90037                 searchWrap
90038                     .call(svgIcon('#iD-icon-search', 'pre-text'));
90039
90040                 if (_autofocus) {
90041                     search.node().focus();
90042                 }
90043
90044                 var listWrap = selection
90045                     .append('div')
90046                     .attr('class', 'inspector-body');
90047
90048                 var list = listWrap
90049                     .append('div')
90050                     .attr('class', 'preset-list')
90051                     .call(drawList, _mainPresetIndex.defaults(entityGeometries()[0], 36, !context.inIntro()));
90052
90053                 context.features().on('change.preset-list', updateForFeatureHiddenState);
90054             }
90055
90056
90057             function drawList(list, presets) {
90058                 presets = presets.matchAllGeometry(entityGeometries());
90059                 var collection = presets.collection.reduce(function(collection, preset) {
90060                     if (!preset) { return collection; }
90061
90062                     if (preset.members) {
90063                         if (preset.members.collection.filter(function(preset) {
90064                             return preset.addable();
90065                         }).length > 1) {
90066                             collection.push(CategoryItem(preset));
90067                         }
90068                     } else if (preset.addable()) {
90069                         collection.push(PresetItem(preset));
90070                     }
90071                     return collection;
90072                 }, []);
90073
90074                 var items = list.selectAll('.preset-list-item')
90075                     .data(collection, function(d) { return d.preset.id; });
90076
90077                 items.order();
90078
90079                 items.exit()
90080                     .remove();
90081
90082                 items.enter()
90083                     .append('div')
90084                     .attr('class', function(item) { return 'preset-list-item preset-' + item.preset.id.replace('/', '-'); })
90085                     .classed('current', function(item) { return _currentPresets.indexOf(item.preset) !== -1; })
90086                     .each(function(item) { select(this).call(item); })
90087                     .style('opacity', 0)
90088                     .transition()
90089                     .style('opacity', 1);
90090
90091                 updateForFeatureHiddenState();
90092             }
90093
90094             function itemKeydown(){
90095                 // the actively focused item
90096                 var item = select(this.closest('.preset-list-item'));
90097                 var parentItem = select(item.node().parentNode.closest('.preset-list-item'));
90098
90099                 // arrow down, move focus to the next, lower item
90100                 if (event.keyCode === utilKeybinding.keyCodes['↓']) {
90101                     event.preventDefault();
90102                     event.stopPropagation();
90103                     // the next item in the list at the same level
90104                     var nextItem = select(item.node().nextElementSibling);
90105                     // if there is no next item in this list
90106                     if (nextItem.empty()) {
90107                         // if there is a parent item
90108                         if (!parentItem.empty()) {
90109                             // the item is the last item of a sublist,
90110                             // select the next item at the parent level
90111                             nextItem = select(parentItem.node().nextElementSibling);
90112                         }
90113                     // if the focused item is expanded
90114                     } else if (select(this).classed('expanded')) {
90115                         // select the first subitem instead
90116                         nextItem = item.select('.subgrid .preset-list-item:first-child');
90117                     }
90118                     if (!nextItem.empty()) {
90119                         // focus on the next item
90120                         nextItem.select('.preset-list-button').node().focus();
90121                     }
90122
90123                 // arrow up, move focus to the previous, higher item
90124                 } else if (event.keyCode === utilKeybinding.keyCodes['↑']) {
90125                     event.preventDefault();
90126                     event.stopPropagation();
90127                     // the previous item in the list at the same level
90128                     var previousItem = select(item.node().previousElementSibling);
90129
90130                     // if there is no previous item in this list
90131                     if (previousItem.empty()) {
90132                         // if there is a parent item
90133                         if (!parentItem.empty()) {
90134                             // the item is the first subitem of a sublist select the parent item
90135                             previousItem = parentItem;
90136                         }
90137                     // if the previous item is expanded
90138                     } else if (previousItem.select('.preset-list-button').classed('expanded')) {
90139                         // select the last subitem of the sublist of the previous item
90140                         previousItem = previousItem.select('.subgrid .preset-list-item:last-child');
90141                     }
90142
90143                     if (!previousItem.empty()) {
90144                         // focus on the previous item
90145                         previousItem.select('.preset-list-button').node().focus();
90146                     } else {
90147                         // the focus is at the top of the list, move focus back to the search field
90148                         var search = select(this.closest('.preset-list-pane')).select('.preset-search-input');
90149                         search.node().focus();
90150                     }
90151
90152                 // arrow left, move focus to the parent item if there is one
90153                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90154                     event.preventDefault();
90155                     event.stopPropagation();
90156                     // if there is a parent item, focus on the parent item
90157                     if (!parentItem.empty()) {
90158                         parentItem.select('.preset-list-button').node().focus();
90159                     }
90160
90161                 // arrow right, choose this item
90162                 } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90163                     event.preventDefault();
90164                     event.stopPropagation();
90165                     item.datum().choose.call(select(this).node());
90166                 }
90167             }
90168
90169
90170             function CategoryItem(preset) {
90171                 var box, sublist, shown = false;
90172
90173                 function item(selection) {
90174                     var wrap = selection.append('div')
90175                         .attr('class', 'preset-list-button-wrap category');
90176
90177                     function click() {
90178                         var isExpanded = select(this).classed('expanded');
90179                         var iconName = isExpanded ?
90180                             (_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward') : '#iD-icon-down';
90181                         select(this)
90182                             .classed('expanded', !isExpanded);
90183                         select(this).selectAll('div.label-inner svg.icon use')
90184                             .attr('href', iconName);
90185                         item.choose();
90186                     }
90187
90188                     var geometries = entityGeometries();
90189
90190                     var button = wrap
90191                         .append('button')
90192                         .attr('class', 'preset-list-button')
90193                         .classed('expanded', false)
90194                         .call(uiPresetIcon()
90195                             .geometry(geometries.length === 1 && geometries[0])
90196                             .preset(preset))
90197                         .on('click', click)
90198                         .on('keydown', function() {
90199                             // right arrow, expand the focused item
90200                             if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '←' : '→']) {
90201                                 event.preventDefault();
90202                                 event.stopPropagation();
90203                                 // if the item isn't expanded
90204                                 if (!select(this).classed('expanded')) {
90205                                     // toggle expansion (expand the item)
90206                                     click.call(this);
90207                                 }
90208                             // left arrow, collapse the focused item
90209                             } else if (event.keyCode === utilKeybinding.keyCodes[(_mainLocalizer.textDirection() === 'rtl') ? '→' : '←']) {
90210                                 event.preventDefault();
90211                                 event.stopPropagation();
90212                                 // if the item is expanded
90213                                 if (select(this).classed('expanded')) {
90214                                     // toggle expansion (collapse the item)
90215                                     click.call(this);
90216                                 }
90217                             } else {
90218                                 itemKeydown.call(this);
90219                             }
90220                         });
90221
90222                     var label = button
90223                         .append('div')
90224                         .attr('class', 'label')
90225                         .append('div')
90226                         .attr('class', 'label-inner');
90227
90228                     label
90229                         .append('div')
90230                         .attr('class', 'namepart')
90231                         .call(svgIcon((_mainLocalizer.textDirection() === 'rtl' ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'))
90232                         .append('span')
90233                         .html(function() { return preset.name() + '&hellip;'; });
90234
90235                     box = selection.append('div')
90236                         .attr('class', 'subgrid')
90237                         .style('max-height', '0px')
90238                         .style('opacity', 0);
90239
90240                     box.append('div')
90241                         .attr('class', 'arrow');
90242
90243                     sublist = box.append('div')
90244                         .attr('class', 'preset-list fillL3');
90245                 }
90246
90247
90248                 item.choose = function() {
90249                     if (!box || !sublist) { return; }
90250
90251                     if (shown) {
90252                         shown = false;
90253                         box.transition()
90254                             .duration(200)
90255                             .style('opacity', '0')
90256                             .style('max-height', '0px')
90257                             .style('padding-bottom', '0px');
90258                     } else {
90259                         shown = true;
90260                         var members = preset.members.matchAllGeometry(entityGeometries());
90261                         sublist.call(drawList, members);
90262                         box.transition()
90263                             .duration(200)
90264                             .style('opacity', '1')
90265                             .style('max-height', 200 + members.collection.length * 190 + 'px')
90266                             .style('padding-bottom', '10px');
90267                     }
90268                 };
90269
90270                 item.preset = preset;
90271                 return item;
90272             }
90273
90274
90275             function PresetItem(preset) {
90276                 function item(selection) {
90277                     var wrap = selection.append('div')
90278                         .attr('class', 'preset-list-button-wrap');
90279
90280                     var geometries = entityGeometries();
90281
90282                     var button = wrap.append('button')
90283                         .attr('class', 'preset-list-button')
90284                         .call(uiPresetIcon()
90285                             .geometry(geometries.length === 1 && geometries[0])
90286                             .preset(preset))
90287                         .on('click', item.choose)
90288                         .on('keydown', itemKeydown);
90289
90290                     var label = button
90291                         .append('div')
90292                         .attr('class', 'label')
90293                         .append('div')
90294                         .attr('class', 'label-inner');
90295
90296                     // NOTE: split/join on en-dash, not a hypen (to avoid conflict with fr - nl names in Brussels etc)
90297                     label.selectAll('.namepart')
90298                         .data(preset.name().split(' – '))
90299                         .enter()
90300                         .append('div')
90301                         .attr('class', 'namepart')
90302                         .text(function(d) { return d; });
90303
90304                     wrap.call(item.reference.button);
90305                     selection.call(item.reference.body);
90306                 }
90307
90308                 item.choose = function() {
90309                     if (select(this).classed('disabled')) { return; }
90310                     if (!context.inIntro()) {
90311                         _mainPresetIndex.setMostRecent(preset, entityGeometries()[0]);
90312                     }
90313                     context.perform(
90314                         function(graph) {
90315                             for (var i in _entityIDs) {
90316                                 var entityID = _entityIDs[i];
90317                                 var oldPreset = _mainPresetIndex.match(graph.entity(entityID), graph);
90318                                 graph = actionChangePreset(entityID, oldPreset, preset)(graph);
90319                             }
90320                             return graph;
90321                         },
90322                         _t('operations.change_tags.annotation')
90323                     );
90324
90325                     context.validator().validate();  // rerun validation
90326                     dispatch$1.call('choose', this, preset);
90327                 };
90328
90329                 item.help = function() {
90330                     event.stopPropagation();
90331                     item.reference.toggle();
90332                 };
90333
90334                 item.preset = preset;
90335                 item.reference = uiTagReference(preset.reference(entityGeometries()[0]));
90336
90337                 return item;
90338             }
90339
90340
90341             function updateForFeatureHiddenState() {
90342                 if (!_entityIDs.every(context.hasEntity)) { return; }
90343
90344                 var geometries = entityGeometries();
90345                 var button = context.container().selectAll('.preset-list .preset-list-button');
90346
90347                 // remove existing tooltips
90348                 button.call(uiTooltip().destroyAny);
90349
90350                 button.each(function(item, index) {
90351                     var hiddenPresetFeaturesId;
90352                     for (var i in geometries) {
90353                         hiddenPresetFeaturesId = context.features().isHiddenPreset(item.preset, geometries[i]);
90354                         if (hiddenPresetFeaturesId) { break; }
90355                     }
90356                     var isHiddenPreset = !context.inIntro() &&
90357                         !!hiddenPresetFeaturesId &&
90358                         (_currentPresets.length !== 1 || item.preset !== _currentPresets[0]);
90359
90360                     select(this)
90361                         .classed('disabled', isHiddenPreset);
90362
90363                     if (isHiddenPreset) {
90364                         var isAutoHidden = context.features().autoHidden(hiddenPresetFeaturesId);
90365                         var tooltipIdSuffix = isAutoHidden ? 'zoom' : 'manual';
90366                         var tooltipObj = { features: _t('feature.' + hiddenPresetFeaturesId + '.description') };
90367                         select(this).call(uiTooltip()
90368                             .title(_t('inspector.hidden_preset.' + tooltipIdSuffix, tooltipObj))
90369                             .placement(index < 2 ? 'bottom' : 'top')
90370                         );
90371                     }
90372                 });
90373             }
90374
90375             presetList.autofocus = function(val) {
90376                 if (!arguments.length) { return _autofocus; }
90377                 _autofocus = val;
90378                 return presetList;
90379             };
90380
90381             presetList.entityIDs = function(val) {
90382                 if (!arguments.length) { return _entityIDs; }
90383                 _entityIDs = val;
90384                 if (_entityIDs && _entityIDs.length) {
90385                     var presets = _entityIDs.map(function(entityID) {
90386                         return _mainPresetIndex.match(context.entity(entityID), context.graph());
90387                     });
90388                     presetList.presets(presets);
90389                 }
90390                 return presetList;
90391             };
90392
90393             presetList.presets = function(val) {
90394                 if (!arguments.length) { return _currentPresets; }
90395                 _currentPresets = val;
90396                 return presetList;
90397             };
90398
90399             function entityGeometries() {
90400
90401                 var counts = {};
90402
90403                 for (var i in _entityIDs) {
90404                     var entityID = _entityIDs[i];
90405                     var entity = context.entity(entityID);
90406                     var geometry = entity.geometry(context.graph());
90407
90408                     // Treat entities on addr:interpolation lines as points, not vertices (#3241)
90409                     if (geometry === 'vertex' && entity.isOnAddressLine(context.graph())) {
90410                         geometry = 'point';
90411                     }
90412
90413                     if (!counts[geometry]) { counts[geometry] = 0; }
90414                     counts[geometry] += 1;
90415                 }
90416
90417                 return Object.keys(counts).sort(function(geom1, geom2) {
90418                     return counts[geom2] - counts[geom1];
90419                 });
90420             }
90421
90422             function combinedEntityExtent() {
90423                 return _entityIDs.reduce(function(extent, entityID) {
90424                     var entity = context.graph().entity(entityID);
90425                     return extent.extend(entity.extent(context.graph()));
90426                 }, geoExtent());
90427             }
90428
90429             return utilRebind(presetList, dispatch$1, 'on');
90430         }
90431
90432         function uiInspector(context) {
90433             var presetList = uiPresetList(context);
90434             var entityEditor = uiEntityEditor(context);
90435             var wrap = select(null),
90436                 presetPane = select(null),
90437                 editorPane = select(null);
90438             var _state = 'select';
90439             var _entityIDs;
90440             var _newFeature = false;
90441
90442
90443             function inspector(selection) {
90444                 presetList
90445                     .entityIDs(_entityIDs)
90446                     .autofocus(_newFeature)
90447                     .on('choose', inspector.setPreset)
90448                     .on('cancel', function() {
90449                         wrap.transition()
90450                             .styleTween('right', function() { return interpolate('-100%', '0%'); });
90451                         editorPane.call(entityEditor);
90452                     });
90453
90454                 entityEditor
90455                     .state(_state)
90456                     .entityIDs(_entityIDs)
90457                     .on('choose', inspector.showList);
90458
90459                 wrap = selection.selectAll('.panewrap')
90460                     .data([0]);
90461
90462                 var enter = wrap.enter()
90463                     .append('div')
90464                     .attr('class', 'panewrap');
90465
90466                 enter
90467                     .append('div')
90468                     .attr('class', 'preset-list-pane pane');
90469
90470                 enter
90471                     .append('div')
90472                     .attr('class', 'entity-editor-pane pane');
90473
90474                 wrap = wrap.merge(enter);
90475                 presetPane = wrap.selectAll('.preset-list-pane');
90476                 editorPane = wrap.selectAll('.entity-editor-pane');
90477
90478                 function shouldDefaultToPresetList() {
90479                     // always show the inspector on hover
90480                     if (_state !== 'select') { return false; }
90481
90482                     // can only change preset on single selection
90483                     if (_entityIDs.length !== 1) { return false; }
90484
90485                     var entityID = _entityIDs[0];
90486                     var entity = context.hasEntity(entityID);
90487                     if (!entity) { return false; }
90488
90489                     // default to inspector if there are already tags
90490                     if (entity.hasNonGeometryTags()) { return false; }
90491
90492                     // prompt to select preset if feature is new and untagged
90493                     if (_newFeature) { return true; }
90494
90495                     // all existing features except vertices should default to inspector
90496                     if (entity.geometry(context.graph()) !== 'vertex') { return false; }
90497
90498                     // show vertex relations if any
90499                     if (context.graph().parentRelations(entity).length) { return false; }
90500
90501                     // show vertex issues if there are any
90502                     if (context.validator().getEntityIssues(entityID).length) { return false; }
90503
90504                     // show turn retriction editor for junction vertices
90505                     if (entity.isHighwayIntersection(context.graph())) { return false; }
90506
90507                     // otherwise show preset list for uninteresting vertices
90508                     return true;
90509                 }
90510
90511                 if (shouldDefaultToPresetList()) {
90512                     wrap.style('right', '-100%');
90513                     presetPane.call(presetList);
90514                 } else {
90515                     wrap.style('right', '0%');
90516                     editorPane.call(entityEditor);
90517                 }
90518
90519                 var footer = selection.selectAll('.footer')
90520                     .data([0]);
90521
90522                 footer = footer.enter()
90523                     .append('div')
90524                     .attr('class', 'footer')
90525                     .merge(footer);
90526
90527                 footer
90528                     .call(uiViewOnOSM(context)
90529                         .what(context.hasEntity(_entityIDs.length === 1 && _entityIDs[0]))
90530                     );
90531             }
90532
90533             inspector.showList = function(presets) {
90534
90535                 wrap.transition()
90536                     .styleTween('right', function() { return interpolate('0%', '-100%'); });
90537
90538                 if (presets) {
90539                     presetList.presets(presets);
90540                 }
90541
90542                 presetPane
90543                     .call(presetList.autofocus(true));
90544             };
90545
90546             inspector.setPreset = function(preset) {
90547
90548                 // upon setting multipolygon, go to the area preset list instead of the editor
90549                 if (preset.id === 'type/multipolygon') {
90550                     presetPane
90551                         .call(presetList.autofocus(true));
90552
90553                 } else {
90554                     wrap.transition()
90555                         .styleTween('right', function() { return interpolate('-100%', '0%'); });
90556
90557                     editorPane
90558                         .call(entityEditor.presets([preset]));
90559                 }
90560
90561             };
90562
90563             inspector.state = function(val) {
90564                 if (!arguments.length) { return _state; }
90565                 _state = val;
90566                 entityEditor.state(_state);
90567
90568                 // remove any old field help overlay that might have gotten attached to the inspector
90569                 context.container().selectAll('.field-help-body').remove();
90570
90571                 return inspector;
90572             };
90573
90574
90575             inspector.entityIDs = function(val) {
90576                 if (!arguments.length) { return _entityIDs; }
90577                 _entityIDs = val;
90578                 return inspector;
90579             };
90580
90581
90582             inspector.newFeature = function(val) {
90583                 if (!arguments.length) { return _newFeature; }
90584                 _newFeature = val;
90585                 return inspector;
90586             };
90587
90588
90589             return inspector;
90590         }
90591
90592         function uiSidebar(context) {
90593             var inspector = uiInspector(context);
90594             var dataEditor = uiDataEditor(context);
90595             var noteEditor = uiNoteEditor(context);
90596             var improveOsmEditor = uiImproveOsmEditor(context);
90597             var keepRightEditor = uiKeepRightEditor(context);
90598             var osmoseEditor = uiOsmoseEditor(context);
90599             var _current;
90600             var _wasData = false;
90601             var _wasNote = false;
90602             var _wasQaItem = false;
90603
90604             // use pointer events on supported platforms; fallback to mouse events
90605             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
90606
90607
90608             function sidebar(selection) {
90609                 var container = context.container();
90610                 var minWidth = 240;
90611                 var sidebarWidth;
90612                 var containerWidth;
90613                 var dragOffset;
90614
90615                 // Set the initial width constraints
90616                 selection
90617                     .style('min-width', minWidth + 'px')
90618                     .style('max-width', '400px')
90619                     .style('width', '33.3333%');
90620
90621                 var resizer = selection
90622                     .append('div')
90623                     .attr('class', 'sidebar-resizer')
90624                     .on(_pointerPrefix + 'down.sidebar-resizer', pointerdown);
90625
90626                 var downPointerId, lastClientX, containerLocGetter;
90627
90628                 function pointerdown() {
90629                     if (downPointerId) { return; }
90630
90631                     if ('button' in event && event.button !== 0) { return; }
90632
90633                     downPointerId = event.pointerId || 'mouse';
90634
90635                     lastClientX = event.clientX;
90636
90637                     containerLocGetter = utilFastMouse(container.node());
90638
90639                     // offset from edge of sidebar-resizer
90640                     dragOffset = utilFastMouse(resizer.node())(event)[0] - 1;
90641
90642                     sidebarWidth = selection.node().getBoundingClientRect().width;
90643                     containerWidth = container.node().getBoundingClientRect().width;
90644                     var widthPct = (sidebarWidth / containerWidth) * 100;
90645                     selection
90646                         .style('width', widthPct + '%')    // lock in current width
90647                         .style('max-width', '85%');        // but allow larger widths
90648
90649                     resizer.classed('dragging', true);
90650
90651                     select(window)
90652                         .on('touchmove.sidebar-resizer', function() {
90653                             // disable page scrolling while resizing on touch input
90654                             event.preventDefault();
90655                         }, { passive: false })
90656                         .on(_pointerPrefix + 'move.sidebar-resizer', pointermove)
90657                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', pointerup);
90658                 }
90659
90660                 function pointermove() {
90661
90662                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90663
90664                     event.preventDefault();
90665
90666                     var dx = event.clientX - lastClientX;
90667
90668                     lastClientX = event.clientX;
90669
90670                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90671                     var scaleX = isRTL ? 0 : 1;
90672                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90673
90674                     var x = containerLocGetter(event)[0] - dragOffset;
90675                     sidebarWidth = isRTL ? containerWidth - x : x;
90676
90677                     var isCollapsed = selection.classed('collapsed');
90678                     var shouldCollapse = sidebarWidth < minWidth;
90679
90680                     selection.classed('collapsed', shouldCollapse);
90681
90682                     if (shouldCollapse) {
90683                         if (!isCollapsed) {
90684                             selection
90685                                 .style(xMarginProperty, '-400px')
90686                                 .style('width', '400px');
90687
90688                             context.ui().onResize([(sidebarWidth - dx) * scaleX, 0]);
90689                         }
90690
90691                     } else {
90692                         var widthPct = (sidebarWidth / containerWidth) * 100;
90693                         selection
90694                             .style(xMarginProperty, null)
90695                             .style('width', widthPct + '%');
90696
90697                         if (isCollapsed) {
90698                             context.ui().onResize([-sidebarWidth * scaleX, 0]);
90699                         } else {
90700                             context.ui().onResize([-dx * scaleX, 0]);
90701                         }
90702                     }
90703                 }
90704
90705                 function pointerup() {
90706                     if (downPointerId !== (event.pointerId || 'mouse')) { return; }
90707
90708                     downPointerId = null;
90709
90710                     resizer.classed('dragging', false);
90711
90712                     select(window)
90713                         .on('touchmove.sidebar-resizer', null)
90714                         .on(_pointerPrefix + 'move.sidebar-resizer', null)
90715                         .on(_pointerPrefix + 'up.sidebar-resizer pointercancel.sidebar-resizer', null);
90716                 }
90717
90718                 var featureListWrap = selection
90719                     .append('div')
90720                     .attr('class', 'feature-list-pane')
90721                     .call(uiFeatureList(context));
90722
90723                 var inspectorWrap = selection
90724                     .append('div')
90725                     .attr('class', 'inspector-hidden inspector-wrap');
90726
90727                 var hoverModeSelect = function(targets) {
90728                     context.container().selectAll('.feature-list-item').classed('hover', false);
90729
90730                     if (context.selectedIDs().length > 1 &&
90731                         targets && targets.length) {
90732
90733                         var elements = context.container().selectAll('.feature-list-item')
90734                             .filter(function (node) {
90735                                 return targets.indexOf(node) !== -1;
90736                             });
90737
90738                         if (!elements.empty()) {
90739                             elements.classed('hover', true);
90740                         }
90741                     }
90742                 };
90743
90744                 sidebar.hoverModeSelect = throttle(hoverModeSelect, 200);
90745
90746                 function hover(targets) {
90747                     var datum = targets && targets.length && targets[0];
90748                     if (datum && datum.__featurehash__) {   // hovering on data
90749                         _wasData = true;
90750                         sidebar
90751                             .show(dataEditor.datum(datum));
90752
90753                         selection.selectAll('.sidebar-component')
90754                             .classed('inspector-hover', true);
90755
90756                     } else if (datum instanceof osmNote) {
90757                         if (context.mode().id === 'drag-note') { return; }
90758                         _wasNote = true;
90759
90760                         var osm = services.osm;
90761                         if (osm) {
90762                             datum = osm.getNote(datum.id);   // marker may contain stale data - get latest
90763                         }
90764
90765                         sidebar
90766                             .show(noteEditor.note(datum));
90767
90768                         selection.selectAll('.sidebar-component')
90769                             .classed('inspector-hover', true);
90770
90771                     } else if (datum instanceof QAItem) {
90772                         _wasQaItem = true;
90773
90774                         var errService = services[datum.service];
90775                         if (errService) {
90776                             // marker may contain stale data - get latest
90777                             datum = errService.getError(datum.id);
90778                         }
90779
90780                         // Currently only three possible services
90781                         var errEditor;
90782                         if (datum.service === 'keepRight') {
90783                             errEditor = keepRightEditor;
90784                         } else if (datum.service === 'osmose') {
90785                             errEditor = osmoseEditor;
90786                         } else {
90787                             errEditor = improveOsmEditor;
90788                         }
90789
90790                         context.container().selectAll('.qaItem.' + datum.service)
90791                             .classed('hover', function(d) { return d.id === datum.id; });
90792
90793                         sidebar
90794                             .show(errEditor.error(datum));
90795
90796                         selection.selectAll('.sidebar-component')
90797                             .classed('inspector-hover', true);
90798
90799                     } else if (!_current && (datum instanceof osmEntity)) {
90800                         featureListWrap
90801                             .classed('inspector-hidden', true);
90802
90803                         inspectorWrap
90804                             .classed('inspector-hidden', false)
90805                             .classed('inspector-hover', true);
90806
90807                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), [datum.id]) || inspector.state() !== 'hover') {
90808                             inspector
90809                                 .state('hover')
90810                                 .entityIDs([datum.id])
90811                                 .newFeature(false);
90812
90813                             inspectorWrap
90814                                 .call(inspector);
90815                         }
90816
90817                     } else if (!_current) {
90818                         featureListWrap
90819                             .classed('inspector-hidden', false);
90820                         inspectorWrap
90821                             .classed('inspector-hidden', true);
90822                         inspector
90823                             .state('hide');
90824
90825                     } else if (_wasData || _wasNote || _wasQaItem) {
90826                         _wasNote = false;
90827                         _wasData = false;
90828                         _wasQaItem = false;
90829                         context.container().selectAll('.note').classed('hover', false);
90830                         context.container().selectAll('.qaItem').classed('hover', false);
90831                         sidebar.hide();
90832                     }
90833                 }
90834
90835                 sidebar.hover = throttle(hover, 200);
90836
90837
90838                 sidebar.intersects = function(extent) {
90839                     var rect = selection.node().getBoundingClientRect();
90840                     return extent.intersects([
90841                         context.projection.invert([0, rect.height]),
90842                         context.projection.invert([rect.width, 0])
90843                     ]);
90844                 };
90845
90846
90847                 sidebar.select = function(ids, newFeature) {
90848                     sidebar.hide();
90849
90850                     if (ids && ids.length) {
90851
90852                         var entity = ids.length === 1 && context.entity(ids[0]);
90853                         if (entity && newFeature && selection.classed('collapsed')) {
90854                             // uncollapse the sidebar
90855                             var extent = entity.extent(context.graph());
90856                             sidebar.expand(sidebar.intersects(extent));
90857                         }
90858
90859                         featureListWrap
90860                             .classed('inspector-hidden', true);
90861
90862                         inspectorWrap
90863                             .classed('inspector-hidden', false)
90864                             .classed('inspector-hover', false);
90865
90866                         if (!inspector.entityIDs() || !utilArrayIdentical(inspector.entityIDs(), ids) || inspector.state() !== 'select') {
90867                             inspector
90868                                 .state('select')
90869                                 .entityIDs(ids)
90870                                 .newFeature(newFeature);
90871
90872                             inspectorWrap
90873                                 .call(inspector);
90874                         }
90875
90876                     } else {
90877                         inspector
90878                             .state('hide');
90879                     }
90880                 };
90881
90882
90883                 sidebar.showPresetList = function() {
90884                     inspector.showList();
90885                 };
90886
90887
90888                 sidebar.show = function(component, element) {
90889                     featureListWrap
90890                         .classed('inspector-hidden', true);
90891                     inspectorWrap
90892                         .classed('inspector-hidden', true);
90893
90894                     if (_current) { _current.remove(); }
90895                     _current = selection
90896                         .append('div')
90897                         .attr('class', 'sidebar-component')
90898                         .call(component, element);
90899                 };
90900
90901
90902                 sidebar.hide = function() {
90903                     featureListWrap
90904                         .classed('inspector-hidden', false);
90905                     inspectorWrap
90906                         .classed('inspector-hidden', true);
90907
90908                     if (_current) { _current.remove(); }
90909                     _current = null;
90910                 };
90911
90912
90913                 sidebar.expand = function(moveMap) {
90914                     if (selection.classed('collapsed')) {
90915                         sidebar.toggle(moveMap);
90916                     }
90917                 };
90918
90919
90920                 sidebar.collapse = function(moveMap) {
90921                     if (!selection.classed('collapsed')) {
90922                         sidebar.toggle(moveMap);
90923                     }
90924                 };
90925
90926
90927                 sidebar.toggle = function(moveMap) {
90928                     var e = event;
90929                     if (e && e.sourceEvent) {
90930                         e.sourceEvent.preventDefault();
90931                     } else if (e) {
90932                         e.preventDefault();
90933                     }
90934
90935                     // Don't allow sidebar to toggle when the user is in the walkthrough.
90936                     if (context.inIntro()) { return; }
90937
90938                     var isCollapsed = selection.classed('collapsed');
90939                     var isCollapsing = !isCollapsed;
90940                     var isRTL = (_mainLocalizer.textDirection() === 'rtl');
90941                     var scaleX = isRTL ? 0 : 1;
90942                     var xMarginProperty = isRTL ? 'margin-right' : 'margin-left';
90943
90944                     sidebarWidth = selection.node().getBoundingClientRect().width;
90945
90946                     // switch from % to px
90947                     selection.style('width', sidebarWidth + 'px');
90948
90949                     var startMargin, endMargin, lastMargin;
90950                     if (isCollapsing) {
90951                         startMargin = lastMargin = 0;
90952                         endMargin = -sidebarWidth;
90953                     } else {
90954                         startMargin = lastMargin = -sidebarWidth;
90955                         endMargin = 0;
90956                     }
90957
90958                     selection.transition()
90959                         .style(xMarginProperty, endMargin + 'px')
90960                         .tween('panner', function() {
90961                             var i = d3_interpolateNumber(startMargin, endMargin);
90962                             return function(t) {
90963                                 var dx = lastMargin - Math.round(i(t));
90964                                 lastMargin = lastMargin - dx;
90965                                 context.ui().onResize(moveMap ? undefined : [dx * scaleX, 0]);
90966                             };
90967                         })
90968                         .on('end', function() {
90969                             selection.classed('collapsed', isCollapsing);
90970
90971                             // switch back from px to %
90972                             if (!isCollapsing) {
90973                                 var containerWidth = container.node().getBoundingClientRect().width;
90974                                 var widthPct = (sidebarWidth / containerWidth) * 100;
90975                                 selection
90976                                     .style(xMarginProperty, null)
90977                                     .style('width', widthPct + '%');
90978                             }
90979                         });
90980                 };
90981
90982                 // toggle the sidebar collapse when double-clicking the resizer
90983                 resizer.on('dblclick', sidebar.toggle);
90984
90985                 // ensure hover sidebar is closed when zooming out beyond editable zoom
90986                 context.map().on('crossEditableZoom.sidebar', function(within) {
90987                     if (!within && !selection.select('.inspector-hover').empty()) {
90988                         hover([]);
90989                     }
90990                 });
90991             }
90992
90993             sidebar.showPresetList = function() {};
90994             sidebar.hover = function() {};
90995             sidebar.hover.cancel = function() {};
90996             sidebar.intersects = function() {};
90997             sidebar.select = function() {};
90998             sidebar.show = function() {};
90999             sidebar.hide = function() {};
91000             sidebar.expand = function() {};
91001             sidebar.collapse = function() {};
91002             sidebar.toggle = function() {};
91003
91004             return sidebar;
91005         }
91006
91007         function uiSourceSwitch(context) {
91008             var keys;
91009
91010
91011             function click() {
91012                 event.preventDefault();
91013
91014                 var osm = context.connection();
91015                 if (!osm) { return; }
91016
91017                 if (context.inIntro()) { return; }
91018
91019                 if (context.history().hasChanges() &&
91020                     !window.confirm(_t('source_switch.lose_changes'))) { return; }
91021
91022                 var isLive = select(this)
91023                     .classed('live');
91024
91025                 isLive = !isLive;
91026                 context.enter(modeBrowse(context));
91027                 context.history().clearSaved();          // remove saved history
91028                 context.flush();                         // remove stored data
91029
91030                 select(this)
91031                     .text(isLive ? _t('source_switch.live') : _t('source_switch.dev'))
91032                     .classed('live', isLive)
91033                     .classed('chip', isLive);
91034
91035                 osm.switch(isLive ? keys[0] : keys[1]);  // switch connection (warning: dispatches 'change' event)
91036             }
91037
91038             var sourceSwitch = function(selection) {
91039                 selection
91040                     .append('a')
91041                     .attr('href', '#')
91042                     .text(_t('source_switch.live'))
91043                     .attr('class', 'live chip')
91044                     .on('click', click);
91045             };
91046
91047
91048             sourceSwitch.keys = function(_) {
91049                 if (!arguments.length) { return keys; }
91050                 keys = _;
91051                 return sourceSwitch;
91052             };
91053
91054
91055             return sourceSwitch;
91056         }
91057
91058         function uiSpinner(context) {
91059             var osm = context.connection();
91060
91061
91062             return function(selection) {
91063                 var img = selection
91064                     .append('img')
91065                     .attr('src', context.imagePath('loader-black.gif'))
91066                     .style('opacity', 0);
91067
91068                 if (osm) {
91069                     osm
91070                         .on('loading.spinner', function() {
91071                             img.transition()
91072                                 .style('opacity', 1);
91073                         })
91074                         .on('loaded.spinner', function() {
91075                             img.transition()
91076                                 .style('opacity', 0);
91077                         });
91078                 }
91079             };
91080         }
91081
91082         function uiSplash(context) {
91083           return function (selection) {
91084             // Exception - if there are restorable changes, skip this splash screen.
91085             // This is because we currently only support one `uiModal` at a time
91086             //  and we need to show them `uiRestore`` instead of this one.
91087             if (context.history().hasRestorableChanges()) { return; }
91088
91089             // If user has not seen this version of the privacy policy, show the splash again.
91090             var updateMessage = '';
91091             var sawPrivacyVersion = corePreferences('sawPrivacyVersion');
91092             var showSplash = !corePreferences('sawSplash');
91093             if (sawPrivacyVersion !== context.privacyVersion) {
91094               updateMessage = _t('splash.privacy_update');
91095               showSplash = true;
91096             }
91097
91098             if (!showSplash) { return; }
91099
91100             corePreferences('sawSplash', true);
91101             corePreferences('sawPrivacyVersion', context.privacyVersion);
91102
91103             // fetch intro graph data now, while user is looking at the splash screen
91104             _mainFileFetcher.get('intro_graph');
91105
91106             var modalSelection = uiModal(selection);
91107
91108             modalSelection.select('.modal')
91109               .attr('class', 'modal-splash modal');
91110
91111             var introModal = modalSelection.select('.content')
91112               .append('div')
91113               .attr('class', 'fillL');
91114
91115             introModal
91116               .append('div')
91117               .attr('class','modal-section')
91118               .append('h3')
91119               .text(_t('splash.welcome'));
91120
91121             var modalSection = introModal
91122               .append('div')
91123               .attr('class','modal-section');
91124
91125             modalSection
91126               .append('p')
91127               .html(_t('splash.text', {
91128                 version: context.version,
91129                 website: '<a target="_blank" href="http://ideditor.blog/">ideditor.blog</a>',
91130                 github: '<a target="_blank" href="https://github.com/openstreetmap/iD">github.com</a>'
91131               }));
91132
91133             modalSection
91134               .append('p')
91135               .html(_t('splash.privacy', {
91136                 updateMessage: updateMessage,
91137                 privacyLink: '<a target="_blank" href="https://github.com/openstreetmap/iD/blob/release/PRIVACY.md">' +
91138                   _t('splash.privacy_policy') + '</a>'
91139               }));
91140
91141             var buttonWrap = introModal
91142               .append('div')
91143               .attr('class', 'modal-actions');
91144
91145             var walkthrough = buttonWrap
91146               .append('button')
91147               .attr('class', 'walkthrough')
91148               .on('click', function () {
91149                 context.container().call(uiIntro(context));
91150                 modalSelection.close();
91151               });
91152
91153             walkthrough
91154               .append('svg')
91155               .attr('class', 'logo logo-walkthrough')
91156               .append('use')
91157               .attr('xlink:href', '#iD-logo-walkthrough');
91158
91159             walkthrough
91160               .append('div')
91161               .text(_t('splash.walkthrough'));
91162
91163             var startEditing = buttonWrap
91164               .append('button')
91165               .attr('class', 'start-editing')
91166               .on('click', modalSelection.close);
91167
91168             startEditing
91169               .append('svg')
91170               .attr('class', 'logo logo-features')
91171               .append('use')
91172               .attr('xlink:href', '#iD-logo-features');
91173
91174             startEditing
91175               .append('div')
91176               .text(_t('splash.start'));
91177
91178             modalSelection.select('button.close')
91179               .attr('class','hide');
91180           };
91181         }
91182
91183         function uiStatus(context) {
91184             var osm = context.connection();
91185
91186
91187             return function(selection) {
91188                 if (!osm) { return; }
91189
91190                 function update(err, apiStatus) {
91191                     selection.html('');
91192
91193                     if (err) {
91194                         if (apiStatus === 'connectionSwitched') {
91195                             // if the connection was just switched, we can't rely on
91196                             // the status (we're getting the status of the previous api)
91197                             return;
91198
91199                         } else if (apiStatus === 'rateLimited') {
91200                             selection
91201                                 .text(_t('osm_api_status.message.rateLimit'))
91202                                 .append('a')
91203                                 .attr('class', 'api-status-login')
91204                                 .attr('target', '_blank')
91205                                 .call(svgIcon('#iD-icon-out-link', 'inline'))
91206                                 .append('span')
91207                                 .text(_t('login'))
91208                                 .on('click.login', function() {
91209                                     event.preventDefault();
91210                                     osm.authenticate();
91211                                 });
91212                         } else {
91213
91214                             // don't allow retrying too rapidly
91215                             var throttledRetry = throttle(function() {
91216                                 // try loading the visible tiles
91217                                 context.loadTiles(context.projection);
91218                                 // manually reload the status too in case all visible tiles were already loaded
91219                                 osm.reloadApiStatus();
91220                             }, 2000);
91221
91222                             // eslint-disable-next-line no-warning-comments
91223                             // TODO: nice messages for different error types
91224                             selection
91225                                 .text(_t('osm_api_status.message.error') + ' ')
91226                                 .append('a')
91227                                 // let the user manually retry their connection directly
91228                                 .text(_t('osm_api_status.retry'))
91229                                 .on('click.retry', function() {
91230                                     event.preventDefault();
91231                                     throttledRetry();
91232                                 });
91233                         }
91234
91235                     } else if (apiStatus === 'readonly') {
91236                         selection.text(_t('osm_api_status.message.readonly'));
91237                     } else if (apiStatus === 'offline') {
91238                         selection.text(_t('osm_api_status.message.offline'));
91239                     }
91240
91241                     selection.attr('class', 'api-status ' + (err ? 'error' : apiStatus));
91242                 }
91243
91244                 osm.on('apiStatusChange.uiStatus', update);
91245
91246                 // reload the status periodically regardless of other factors
91247                 window.setInterval(function() {
91248                     osm.reloadApiStatus();
91249                 }, 90000);
91250
91251                 // load the initial status in case no OSM data was loaded yet
91252                 osm.reloadApiStatus();
91253             };
91254         }
91255
91256         function modeDrawArea(context, wayID, startGraph, button) {
91257             var mode = {
91258                 button: button,
91259                 id: 'draw-area'
91260             };
91261
91262             var behavior = behaviorDrawWay(context, wayID, mode, startGraph)
91263                 .on('rejectedSelfIntersection.modeDrawArea', function() {
91264                     context.ui().flash
91265                         .text(_t('self_intersection.error.areas'))();
91266                 });
91267
91268             mode.wayID = wayID;
91269
91270             mode.enter = function() {
91271                 context.install(behavior);
91272             };
91273
91274             mode.exit = function() {
91275                 context.uninstall(behavior);
91276             };
91277
91278             mode.selectedIDs = function() {
91279                 return [wayID];
91280             };
91281
91282             mode.activeID = function() {
91283                 return (behavior && behavior.activeID()) || [];
91284             };
91285
91286             return mode;
91287         }
91288
91289         function modeAddArea(context, mode) {
91290             mode.id = 'add-area';
91291
91292             var behavior = behaviorAddWay(context)
91293                 .on('start', start)
91294                 .on('startFromWay', startFromWay)
91295                 .on('startFromNode', startFromNode);
91296
91297             var defaultTags = { area: 'yes' };
91298             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'area'); }
91299
91300
91301             function actionClose(wayId) {
91302                 return function (graph) {
91303                     return graph.replace(graph.entity(wayId).close());
91304                 };
91305             }
91306
91307
91308             function start(loc) {
91309                 var startGraph = context.graph();
91310                 var node = osmNode({ loc: loc });
91311                 var way = osmWay({ tags: defaultTags });
91312
91313                 context.perform(
91314                     actionAddEntity(node),
91315                     actionAddEntity(way),
91316                     actionAddVertex(way.id, node.id),
91317                     actionClose(way.id)
91318                 );
91319
91320                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91321             }
91322
91323
91324             function startFromWay(loc, edge) {
91325                 var startGraph = context.graph();
91326                 var node = osmNode({ loc: loc });
91327                 var way = osmWay({ tags: defaultTags });
91328
91329                 context.perform(
91330                     actionAddEntity(node),
91331                     actionAddEntity(way),
91332                     actionAddVertex(way.id, node.id),
91333                     actionClose(way.id),
91334                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91335                 );
91336
91337                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91338             }
91339
91340
91341             function startFromNode(node) {
91342                 var startGraph = context.graph();
91343                 var way = osmWay({ tags: defaultTags });
91344
91345                 context.perform(
91346                     actionAddEntity(way),
91347                     actionAddVertex(way.id, node.id),
91348                     actionClose(way.id)
91349                 );
91350
91351                 context.enter(modeDrawArea(context, way.id, startGraph, mode.button));
91352             }
91353
91354
91355             mode.enter = function() {
91356                 context.install(behavior);
91357             };
91358
91359
91360             mode.exit = function() {
91361                 context.uninstall(behavior);
91362             };
91363
91364
91365             return mode;
91366         }
91367
91368         function modeAddLine(context, mode) {
91369             mode.id = 'add-line';
91370
91371             var behavior = behaviorAddWay(context)
91372                 .on('start', start)
91373                 .on('startFromWay', startFromWay)
91374                 .on('startFromNode', startFromNode);
91375
91376             var defaultTags = {};
91377             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'line'); }
91378
91379
91380             function start(loc) {
91381                 var startGraph = context.graph();
91382                 var node = osmNode({ loc: loc });
91383                 var way = osmWay({ tags: defaultTags });
91384
91385                 context.perform(
91386                     actionAddEntity(node),
91387                     actionAddEntity(way),
91388                     actionAddVertex(way.id, node.id)
91389                 );
91390
91391                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91392             }
91393
91394
91395             function startFromWay(loc, edge) {
91396                 var startGraph = context.graph();
91397                 var node = osmNode({ loc: loc });
91398                 var way = osmWay({ tags: defaultTags });
91399
91400                 context.perform(
91401                     actionAddEntity(node),
91402                     actionAddEntity(way),
91403                     actionAddVertex(way.id, node.id),
91404                     actionAddMidpoint({ loc: loc, edge: edge }, node)
91405                 );
91406
91407                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91408             }
91409
91410
91411             function startFromNode(node) {
91412                 var startGraph = context.graph();
91413                 var way = osmWay({ tags: defaultTags });
91414
91415                 context.perform(
91416                     actionAddEntity(way),
91417                     actionAddVertex(way.id, node.id)
91418                 );
91419
91420                 context.enter(modeDrawLine(context, way.id, startGraph, mode.button));
91421             }
91422
91423
91424             mode.enter = function() {
91425                 context.install(behavior);
91426             };
91427
91428
91429             mode.exit = function() {
91430                 context.uninstall(behavior);
91431             };
91432
91433             return mode;
91434         }
91435
91436         function modeAddPoint(context, mode) {
91437
91438             mode.id = 'add-point';
91439
91440             var behavior = behaviorDraw(context)
91441                 .on('click', add)
91442                 .on('clickWay', addWay)
91443                 .on('clickNode', addNode)
91444                 .on('cancel', cancel)
91445                 .on('finish', cancel);
91446
91447             var defaultTags = {};
91448             if (mode.preset) { defaultTags = mode.preset.setTags(defaultTags, 'point'); }
91449
91450
91451             function add(loc) {
91452                 var node = osmNode({ loc: loc, tags: defaultTags });
91453
91454                 context.perform(
91455                     actionAddEntity(node),
91456                     _t('operations.add.annotation.point')
91457                 );
91458
91459                 enterSelectMode(node);
91460             }
91461
91462
91463             function addWay(loc, edge) {
91464                 var node = osmNode({ tags: defaultTags });
91465
91466                 context.perform(
91467                     actionAddMidpoint({loc: loc, edge: edge}, node),
91468                     _t('operations.add.annotation.vertex')
91469                 );
91470
91471                 enterSelectMode(node);
91472             }
91473
91474             function enterSelectMode(node) {
91475                 context.enter(
91476                     modeSelect(context, [node.id]).newFeature(true)
91477                 );
91478             }
91479
91480
91481             function addNode(node) {
91482                 if (Object.keys(defaultTags).length === 0) {
91483                     enterSelectMode(node);
91484                     return;
91485                 }
91486
91487                 var tags = Object.assign({}, node.tags);  // shallow copy
91488                 for (var key in defaultTags) {
91489                     tags[key] = defaultTags[key];
91490                 }
91491
91492                 context.perform(
91493                     actionChangeTags(node.id, tags),
91494                     _t('operations.add.annotation.point')
91495                 );
91496
91497                 enterSelectMode(node);
91498             }
91499
91500
91501             function cancel() {
91502                 context.enter(modeBrowse(context));
91503             }
91504
91505
91506             mode.enter = function() {
91507                 context.install(behavior);
91508             };
91509
91510
91511             mode.exit = function() {
91512                 context.uninstall(behavior);
91513             };
91514
91515
91516             return mode;
91517         }
91518
91519         function modeAddNote(context) {
91520             var mode = {
91521                 id: 'add-note',
91522                 button: 'note',
91523                 title: _t('modes.add_note.title'),
91524                 description: _t('modes.add_note.description'),
91525                 key: _t('modes.add_note.key')
91526             };
91527
91528             var behavior = behaviorDraw(context)
91529                 .on('click', add)
91530                 .on('cancel', cancel)
91531                 .on('finish', cancel);
91532
91533
91534             function add(loc) {
91535                 var osm = services.osm;
91536                 if (!osm) { return; }
91537
91538                 var note = osmNote({ loc: loc, status: 'open', comments: [] });
91539                 osm.replaceNote(note);
91540
91541                 // force a reraw (there is no history change that would otherwise do this)
91542                 context.map().pan([0,0]);
91543
91544                 context
91545                     .selectedNoteID(note.id)
91546                     .enter(modeSelectNote(context, note.id).newFeature(true));
91547             }
91548
91549
91550             function cancel() {
91551                 context.enter(modeBrowse(context));
91552             }
91553
91554
91555             mode.enter = function() {
91556                 context.install(behavior);
91557             };
91558
91559
91560             mode.exit = function() {
91561                 context.uninstall(behavior);
91562             };
91563
91564
91565             return mode;
91566         }
91567
91568         function uiConflicts(context) {
91569             var dispatch$1 = dispatch('cancel', 'save');
91570             var keybinding = utilKeybinding('conflicts');
91571             var _origChanges;
91572             var _conflictList;
91573             var _shownConflictIndex;
91574
91575
91576             function keybindingOn() {
91577                 select(document)
91578                     .call(keybinding.on('⎋', cancel, true));
91579             }
91580
91581             function keybindingOff() {
91582                 select(document)
91583                     .call(keybinding.unbind);
91584             }
91585
91586             function tryAgain() {
91587                 keybindingOff();
91588                 dispatch$1.call('save');
91589             }
91590
91591             function cancel() {
91592                 keybindingOff();
91593                 dispatch$1.call('cancel');
91594             }
91595
91596
91597             function conflicts(selection) {
91598                 keybindingOn();
91599
91600                 var headerEnter = selection.selectAll('.header')
91601                     .data([0])
91602                     .enter()
91603                     .append('div')
91604                     .attr('class', 'header fillL');
91605
91606                 headerEnter
91607                     .append('button')
91608                     .attr('class', 'fr')
91609                     .on('click', cancel)
91610                     .call(svgIcon('#iD-icon-close'));
91611
91612                 headerEnter
91613                     .append('h3')
91614                     .text(_t('save.conflict.header'));
91615
91616                 var bodyEnter = selection.selectAll('.body')
91617                     .data([0])
91618                     .enter()
91619                     .append('div')
91620                     .attr('class', 'body fillL');
91621
91622                 var conflictsHelpEnter = bodyEnter
91623                     .append('div')
91624                     .attr('class', 'conflicts-help')
91625                     .text(_t('save.conflict.help'));
91626
91627
91628                 // Download changes link
91629                 var detected = utilDetect();
91630                 var changeset = new osmChangeset();
91631
91632                 delete changeset.id;  // Export without changeset_id
91633
91634                 var data = JXON.stringify(changeset.osmChangeJXON(_origChanges));
91635                 var blob = new Blob([data], { type: 'text/xml;charset=utf-8;' });
91636                 var fileName = 'changes.osc';
91637
91638                 var linkEnter = conflictsHelpEnter.selectAll('.download-changes')
91639                     .append('a')
91640                     .attr('class', 'download-changes');
91641
91642                 if (detected.download) {      // All except IE11 and Edge
91643                     linkEnter                 // download the data as a file
91644                         .attr('href', window.URL.createObjectURL(blob))
91645                         .attr('download', fileName);
91646
91647                 } else {                      // IE11 and Edge
91648                     linkEnter                 // open data uri in a new tab
91649                         .attr('target', '_blank')
91650                         .on('click.download', function() {
91651                             navigator.msSaveBlob(blob, fileName);
91652                         });
91653                 }
91654
91655                 linkEnter
91656                     .call(svgIcon('#iD-icon-load', 'inline'))
91657                     .append('span')
91658                     .text(_t('save.conflict.download_changes'));
91659
91660
91661                 bodyEnter
91662                     .append('div')
91663                     .attr('class', 'conflict-container fillL3')
91664                     .call(showConflict, 0);
91665
91666                 bodyEnter
91667                     .append('div')
91668                     .attr('class', 'conflicts-done')
91669                     .attr('opacity', 0)
91670                     .style('display', 'none')
91671                     .text(_t('save.conflict.done'));
91672
91673                 var buttonsEnter = bodyEnter
91674                     .append('div')
91675                     .attr('class','buttons col12 joined conflicts-buttons');
91676
91677                 buttonsEnter
91678                     .append('button')
91679                     .attr('disabled', _conflictList.length > 1)
91680                     .attr('class', 'action conflicts-button col6')
91681                     .text(_t('save.title'))
91682                     .on('click.try_again', tryAgain);
91683
91684                 buttonsEnter
91685                     .append('button')
91686                     .attr('class', 'secondary-action conflicts-button col6')
91687                     .text(_t('confirm.cancel'))
91688                     .on('click.cancel', cancel);
91689             }
91690
91691
91692             function showConflict(selection, index) {
91693                 index = utilWrap(index, _conflictList.length);
91694                 _shownConflictIndex = index;
91695
91696                 var parent = select(selection.node().parentNode);
91697
91698                 // enable save button if this is the last conflict being reviewed..
91699                 if (index === _conflictList.length - 1) {
91700                     window.setTimeout(function() {
91701                         parent.select('.conflicts-button')
91702                             .attr('disabled', null);
91703
91704                         parent.select('.conflicts-done')
91705                             .transition()
91706                             .attr('opacity', 1)
91707                             .style('display', 'block');
91708                     }, 250);
91709                 }
91710
91711                 var conflict = selection
91712                     .selectAll('.conflict')
91713                     .data([_conflictList[index]]);
91714
91715                 conflict.exit()
91716                     .remove();
91717
91718                 var conflictEnter = conflict.enter()
91719                     .append('div')
91720                     .attr('class', 'conflict');
91721
91722                 conflictEnter
91723                     .append('h4')
91724                     .attr('class', 'conflict-count')
91725                     .text(_t('save.conflict.count', { num: index + 1, total: _conflictList.length }));
91726
91727                 conflictEnter
91728                     .append('a')
91729                     .attr('class', 'conflict-description')
91730                     .attr('href', '#')
91731                     .text(function(d) { return d.name; })
91732                     .on('click', function(d) {
91733                         event.preventDefault();
91734                         zoomToEntity(d.id);
91735                     });
91736
91737                 var details = conflictEnter
91738                     .append('div')
91739                     .attr('class', 'conflict-detail-container');
91740
91741                 details
91742                     .append('ul')
91743                     .attr('class', 'conflict-detail-list')
91744                     .selectAll('li')
91745                     .data(function(d) { return d.details || []; })
91746                     .enter()
91747                     .append('li')
91748                     .attr('class', 'conflict-detail-item')
91749                     .html(function(d) { return d; });
91750
91751                 details
91752                     .append('div')
91753                     .attr('class', 'conflict-choices')
91754                     .call(addChoices);
91755
91756                 details
91757                     .append('div')
91758                     .attr('class', 'conflict-nav-buttons joined cf')
91759                     .selectAll('button')
91760                     .data(['previous', 'next'])
91761                     .enter()
91762                     .append('button')
91763                     .text(function(d) { return _t('save.conflict.' + d); })
91764                     .attr('class', 'conflict-nav-button action col6')
91765                     .attr('disabled', function(d, i) {
91766                         return (i === 0 && index === 0) ||
91767                             (i === 1 && index === _conflictList.length - 1) || null;
91768                     })
91769                     .on('click', function(d, i) {
91770                         event.preventDefault();
91771
91772                         var container = parent.selectAll('.conflict-container');
91773                         var sign = (i === 0 ? -1 : 1);
91774
91775                         container
91776                             .selectAll('.conflict')
91777                             .remove();
91778
91779                         container
91780                             .call(showConflict, index + sign);
91781                     });
91782
91783             }
91784
91785
91786             function addChoices(selection) {
91787                 var choices = selection
91788                     .append('ul')
91789                     .attr('class', 'layer-list')
91790                     .selectAll('li')
91791                     .data(function(d) { return d.choices || []; });
91792
91793                 // enter
91794                 var choicesEnter = choices.enter()
91795                     .append('li')
91796                     .attr('class', 'layer');
91797
91798                 var labelEnter = choicesEnter
91799                     .append('label');
91800
91801                 labelEnter
91802                     .append('input')
91803                     .attr('type', 'radio')
91804                     .attr('name', function(d) { return d.id; })
91805                     .on('change', function(d, i) {
91806                         var ul = this.parentNode.parentNode.parentNode;
91807                         ul.__data__.chosen = i;
91808                         choose(ul, d);
91809                     });
91810
91811                 labelEnter
91812                     .append('span')
91813                     .text(function(d) { return d.text; });
91814
91815                 // update
91816                 choicesEnter
91817                     .merge(choices)
91818                     .each(function(d, i) {
91819                         var ul = this.parentNode;
91820                         if (ul.__data__.chosen === i) {
91821                             choose(ul, d);
91822                         }
91823                     });
91824             }
91825
91826
91827             function choose(ul, datum) {
91828                 if (event) { event.preventDefault(); }
91829
91830                 select(ul)
91831                     .selectAll('li')
91832                     .classed('active', function(d) { return d === datum; })
91833                     .selectAll('input')
91834                     .property('checked', function(d) { return d === datum; });
91835
91836                 var extent = geoExtent();
91837                 var entity;
91838
91839                 entity = context.graph().hasEntity(datum.id);
91840                 if (entity) { extent._extend(entity.extent(context.graph())); }
91841
91842                 datum.action();
91843
91844                 entity = context.graph().hasEntity(datum.id);
91845                 if (entity) { extent._extend(entity.extent(context.graph())); }
91846
91847                 zoomToEntity(datum.id, extent);
91848             }
91849
91850
91851             function zoomToEntity(id, extent) {
91852                 context.surface().selectAll('.hover')
91853                     .classed('hover', false);
91854
91855                 var entity = context.graph().hasEntity(id);
91856                 if (entity) {
91857                     if (extent) {
91858                         context.map().trimmedExtent(extent);
91859                     } else {
91860                         context.map().zoomToEase(entity);
91861                     }
91862                     context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
91863                         .classed('hover', true);
91864                 }
91865             }
91866
91867
91868             // The conflict list should be an array of objects like:
91869             // {
91870             //     id: id,
91871             //     name: entityName(local),
91872             //     details: merge.conflicts(),
91873             //     chosen: 1,
91874             //     choices: [
91875             //         choice(id, keepMine, forceLocal),
91876             //         choice(id, keepTheirs, forceRemote)
91877             //     ]
91878             // }
91879             conflicts.conflictList = function(_) {
91880                 if (!arguments.length) { return _conflictList; }
91881                 _conflictList = _;
91882                 return conflicts;
91883             };
91884
91885
91886             conflicts.origChanges = function(_) {
91887                 if (!arguments.length) { return _origChanges; }
91888                 _origChanges = _;
91889                 return conflicts;
91890             };
91891
91892
91893             conflicts.shownEntityIds = function() {
91894                 if (_conflictList && typeof _shownConflictIndex === 'number') {
91895                     return [_conflictList[_shownConflictIndex].id];
91896                 }
91897                 return [];
91898             };
91899
91900
91901             return utilRebind(conflicts, dispatch$1, 'on');
91902         }
91903
91904         function uiConfirm(selection) {
91905             var modalSelection = uiModal(selection);
91906
91907             modalSelection.select('.modal')
91908                 .classed('modal-alert', true);
91909
91910             var section = modalSelection.select('.content');
91911
91912             section.append('div')
91913                 .attr('class', 'modal-section header');
91914
91915             section.append('div')
91916                 .attr('class', 'modal-section message-text');
91917
91918             var buttons = section.append('div')
91919                 .attr('class', 'modal-section buttons cf');
91920
91921
91922             modalSelection.okButton = function() {
91923                 buttons
91924                     .append('button')
91925                     .attr('class', 'button ok-button action')
91926                     .on('click.confirm', function() {
91927                         modalSelection.remove();
91928                     })
91929                     .text(_t('confirm.okay'))
91930                     .node()
91931                     .focus();
91932
91933                 return modalSelection;
91934             };
91935
91936
91937             return modalSelection;
91938         }
91939
91940         function uiChangesetEditor(context) {
91941             var dispatch$1 = dispatch('change');
91942             var formFields = uiFormFields(context);
91943             var commentCombo = uiCombobox(context, 'comment').caseSensitive(true);
91944             var _fieldsArr;
91945             var _tags;
91946             var _changesetID;
91947
91948
91949             function changesetEditor(selection) {
91950                 render(selection);
91951             }
91952
91953
91954             function render(selection) {
91955                 var initial = false;
91956
91957                 if (!_fieldsArr) {
91958                     initial = true;
91959                     var presets = _mainPresetIndex;
91960
91961                     _fieldsArr = [
91962                         uiField(context, presets.field('comment'), null, { show: true, revert: false }),
91963                         uiField(context, presets.field('source'), null, { show: false, revert: false }),
91964                         uiField(context, presets.field('hashtags'), null, { show: false, revert: false }) ];
91965
91966                     _fieldsArr.forEach(function(field) {
91967                         field
91968                             .on('change', function(t, onInput) {
91969                                 dispatch$1.call('change', field, undefined, t, onInput);
91970                             });
91971                     });
91972                 }
91973
91974                 _fieldsArr.forEach(function(field) {
91975                     field
91976                         .tags(_tags);
91977                 });
91978
91979
91980                 selection
91981                     .call(formFields.fieldsArr(_fieldsArr));
91982
91983
91984                 if (initial) {
91985                     var commentField = selection.select('.form-field-comment textarea');
91986                     var commentNode = commentField.node();
91987
91988                     if (commentNode) {
91989                         commentNode.focus();
91990                         commentNode.select();
91991                     }
91992
91993                     // trigger a 'blur' event so that comment field can be cleaned
91994                     // and checked for hashtags, even if retrieved from localstorage
91995                     utilTriggerEvent(commentField, 'blur');
91996
91997                     var osm = context.connection();
91998                     if (osm) {
91999                         osm.userChangesets(function (err, changesets) {
92000                             if (err) { return; }
92001
92002                             var comments = changesets.map(function(changeset) {
92003                                 var comment = changeset.tags.comment;
92004                                 return comment ? { title: comment, value: comment } : null;
92005                             }).filter(Boolean);
92006
92007                             commentField
92008                                 .call(commentCombo
92009                                     .data(utilArrayUniqBy(comments, 'title'))
92010                                 );
92011                         });
92012                     }
92013                 }
92014
92015                 // Add warning if comment mentions Google
92016                 var hasGoogle = _tags.comment.match(/google/i);
92017                 var commentWarning = selection.select('.form-field-comment').selectAll('.comment-warning')
92018                     .data(hasGoogle ? [0] : []);
92019
92020                 commentWarning.exit()
92021                     .transition()
92022                     .duration(200)
92023                     .style('opacity', 0)
92024                     .remove();
92025
92026                 var commentEnter = commentWarning.enter()
92027                     .insert('div', '.tag-reference-body')
92028                     .attr('class', 'field-warning comment-warning')
92029                     .style('opacity', 0);
92030
92031                 commentEnter
92032                     .append('a')
92033                     .attr('target', '_blank')
92034                     .attr('tabindex', -1)
92035                     .call(svgIcon('#iD-icon-alert', 'inline'))
92036                     .attr('href', _t('commit.google_warning_link'))
92037                     .append('span')
92038                     .text(_t('commit.google_warning'));
92039
92040                 commentEnter
92041                     .transition()
92042                     .duration(200)
92043                     .style('opacity', 1);
92044             }
92045
92046
92047             changesetEditor.tags = function(_) {
92048                 if (!arguments.length) { return _tags; }
92049                 _tags = _;
92050                 // Don't reset _fieldsArr here.
92051                 return changesetEditor;
92052             };
92053
92054
92055             changesetEditor.changesetID = function(_) {
92056                 if (!arguments.length) { return _changesetID; }
92057                 if (_changesetID === _) { return changesetEditor; }
92058                 _changesetID = _;
92059                 _fieldsArr = null;
92060                 return changesetEditor;
92061             };
92062
92063
92064             return utilRebind(changesetEditor, dispatch$1, 'on');
92065         }
92066
92067         function uiSectionChanges(context) {
92068             var detected = utilDetect();
92069
92070             var _discardTags = {};
92071             _mainFileFetcher.get('discarded')
92072                 .then(function(d) { _discardTags = d; })
92073                 .catch(function() { /* ignore */ });
92074
92075             var section = uiSection('changes-list', context)
92076                 .title(function() {
92077                     var history = context.history();
92078                     var summary = history.difference().summary();
92079                     return _t('commit.changes', { count: summary.length });
92080                 })
92081                 .disclosureContent(renderDisclosureContent);
92082
92083             function renderDisclosureContent(selection) {
92084                 var history = context.history();
92085                 var summary = history.difference().summary();
92086
92087                 var container = selection.selectAll('.commit-section')
92088                     .data([0]);
92089
92090                 var containerEnter = container.enter()
92091                     .append('div')
92092                     .attr('class', 'commit-section');
92093
92094                 containerEnter
92095                     .append('ul')
92096                     .attr('class', 'changeset-list');
92097
92098                 container = containerEnter
92099                     .merge(container);
92100
92101
92102                 var items = container.select('ul').selectAll('li')
92103                     .data(summary);
92104
92105                 var itemsEnter = items.enter()
92106                     .append('li')
92107                     .attr('class', 'change-item');
92108
92109                 itemsEnter
92110                     .each(function(d) {
92111                         select(this)
92112                             .call(svgIcon('#iD-icon-' + d.entity.geometry(d.graph), 'pre-text ' + d.changeType));
92113                     });
92114
92115                 itemsEnter
92116                     .append('span')
92117                     .attr('class', 'change-type')
92118                     .text(function(d) { return _t('commit.' + d.changeType) + ' '; });
92119
92120                 itemsEnter
92121                     .append('strong')
92122                     .attr('class', 'entity-type')
92123                     .text(function(d) {
92124                         var matched = _mainPresetIndex.match(d.entity, d.graph);
92125                         return (matched && matched.name()) || utilDisplayType(d.entity.id);
92126                     });
92127
92128                 itemsEnter
92129                     .append('span')
92130                     .attr('class', 'entity-name')
92131                     .text(function(d) {
92132                         var name = utilDisplayName(d.entity) || '',
92133                             string = '';
92134                         if (name !== '') {
92135                             string += ':';
92136                         }
92137                         return string += ' ' + name;
92138                     });
92139
92140                 itemsEnter
92141                     .style('opacity', 0)
92142                     .transition()
92143                     .style('opacity', 1);
92144
92145                 items = itemsEnter
92146                     .merge(items);
92147
92148                 items
92149                     .on('mouseover', mouseover)
92150                     .on('mouseout', mouseout)
92151                     .on('click', click);
92152
92153
92154                 // Download changeset link
92155                 var changeset = new osmChangeset().update({ id: undefined });
92156                 var changes = history.changes(actionDiscardTags(history.difference(), _discardTags));
92157
92158                 delete changeset.id;  // Export without chnageset_id
92159
92160                 var data = JXON.stringify(changeset.osmChangeJXON(changes));
92161                 var blob = new Blob([data], {type: 'text/xml;charset=utf-8;'});
92162                 var fileName = 'changes.osc';
92163
92164                 var linkEnter = container.selectAll('.download-changes')
92165                     .data([0])
92166                     .enter()
92167                     .append('a')
92168                     .attr('class', 'download-changes');
92169
92170                 if (detected.download) {      // All except IE11 and Edge
92171                     linkEnter                 // download the data as a file
92172                         .attr('href', window.URL.createObjectURL(blob))
92173                         .attr('download', fileName);
92174
92175                 } else {                      // IE11 and Edge
92176                     linkEnter                 // open data uri in a new tab
92177                         .attr('target', '_blank')
92178                         .on('click.download', function() {
92179                             navigator.msSaveBlob(blob, fileName);
92180                         });
92181                 }
92182
92183                 linkEnter
92184                     .call(svgIcon('#iD-icon-load', 'inline'))
92185                     .append('span')
92186                     .text(_t('commit.download_changes'));
92187
92188
92189                 function mouseover(d) {
92190                     if (d.entity) {
92191                         context.surface().selectAll(
92192                             utilEntityOrMemberSelector([d.entity.id], context.graph())
92193                         ).classed('hover', true);
92194                     }
92195                 }
92196
92197
92198                 function mouseout() {
92199                     context.surface().selectAll('.hover')
92200                         .classed('hover', false);
92201                 }
92202
92203
92204                 function click(change) {
92205                     if (change.changeType !== 'deleted') {
92206                         var entity = change.entity;
92207                         context.map().zoomToEase(entity);
92208                         context.surface().selectAll(utilEntityOrMemberSelector([entity.id], context.graph()))
92209                             .classed('hover', true);
92210                     }
92211                 }
92212             }
92213
92214             return section;
92215         }
92216
92217         function uiCommitWarnings(context) {
92218
92219             function commitWarnings(selection) {
92220                 var issuesBySeverity = context.validator()
92221                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeDisabledRules: true });
92222
92223                 for (var severity in issuesBySeverity) {
92224                     var issues = issuesBySeverity[severity];
92225                     var section = severity + '-section';
92226                     var issueItem = severity + '-item';
92227
92228                     var container = selection.selectAll('.' + section)
92229                         .data(issues.length ? [0] : []);
92230
92231                     container.exit()
92232                         .remove();
92233
92234                     var containerEnter = container.enter()
92235                         .append('div')
92236                         .attr('class', 'modal-section ' + section + ' fillL2');
92237
92238                     containerEnter
92239                         .append('h3')
92240                         .text(severity === 'warning' ? _t('commit.warnings') : _t('commit.errors'));
92241
92242                     containerEnter
92243                         .append('ul')
92244                         .attr('class', 'changeset-list');
92245
92246                     container = containerEnter
92247                         .merge(container);
92248
92249
92250                     var items = container.select('ul').selectAll('li')
92251                         .data(issues, function(d) { return d.id; });
92252
92253                     items.exit()
92254                         .remove();
92255
92256                     var itemsEnter = items.enter()
92257                         .append('li')
92258                         .attr('class', issueItem);
92259
92260                     itemsEnter
92261                         .call(svgIcon('#iD-icon-alert', 'pre-text'));
92262
92263                     itemsEnter
92264                         .append('strong')
92265                         .attr('class', 'issue-message');
92266
92267                     itemsEnter.filter(function(d) { return d.tooltip; })
92268                         .call(uiTooltip()
92269                             .title(function(d) { return d.tooltip; })
92270                             .placement('top')
92271                         );
92272
92273                     items = itemsEnter
92274                         .merge(items);
92275
92276                     items.selectAll('.issue-message')
92277                         .text(function(d) {
92278                             return d.message(context);
92279                         });
92280
92281                     items
92282                         .on('mouseover', function(d) {
92283                             if (d.entityIds) {
92284                                 context.surface().selectAll(
92285                                     utilEntityOrMemberSelector(
92286                                         d.entityIds,
92287                                         context.graph()
92288                                     )
92289                                 ).classed('hover', true);
92290                             }
92291                         })
92292                         .on('mouseout', function() {
92293                             context.surface().selectAll('.hover')
92294                                 .classed('hover', false);
92295                         })
92296                         .on('click', function(d) {
92297                             context.validator().focusIssue(d);
92298                         });
92299                 }
92300             }
92301
92302
92303             return commitWarnings;
92304         }
92305
92306         var readOnlyTags = [
92307             /^changesets_count$/,
92308             /^created_by$/,
92309             /^ideditor:/,
92310             /^imagery_used$/,
92311             /^host$/,
92312             /^locale$/,
92313             /^warnings:/,
92314             /^resolved:/,
92315             /^closed:note$/,
92316             /^closed:keepright$/,
92317             /^closed:improveosm:/,
92318             /^closed:osmose:/
92319         ];
92320
92321         // treat most punctuation (except -, _, +, &) as hashtag delimiters - #4398
92322         // from https://stackoverflow.com/a/25575009
92323         var hashtagRegex = /(#[^\u2000-\u206F\u2E00-\u2E7F\s\\'!"#$%()*,.\/:;<=>?@\[\]^`{|}~]+)/g;
92324
92325
92326         function uiCommit(context) {
92327             var dispatch$1 = dispatch('cancel');
92328             var _userDetails;
92329             var _selection;
92330
92331             var changesetEditor = uiChangesetEditor(context)
92332                 .on('change', changeTags);
92333             var rawTagEditor = uiSectionRawTagEditor('changeset-tag-editor', context)
92334                 .on('change', changeTags)
92335                 .readOnlyTags(readOnlyTags);
92336             var commitChanges = uiSectionChanges(context);
92337             var commitWarnings = uiCommitWarnings(context);
92338
92339
92340             function commit(selection) {
92341                 _selection = selection;
92342
92343                 // Initialize changeset if one does not exist yet.
92344                 if (!context.changeset) { initChangeset(); }
92345
92346                 loadDerivedChangesetTags();
92347
92348                 selection.call(render);
92349             }
92350
92351             function initChangeset() {
92352
92353                 // expire stored comment, hashtags, source after cutoff datetime - #3947 #4899
92354                 var commentDate = +corePreferences('commentDate') || 0;
92355                 var currDate = Date.now();
92356                 var cutoff = 2 * 86400 * 1000;   // 2 days
92357                 if (commentDate > currDate || currDate - commentDate > cutoff) {
92358                     corePreferences('comment', null);
92359                     corePreferences('hashtags', null);
92360                     corePreferences('source', null);
92361                 }
92362
92363                 // load in explicitly-set values, if any
92364                 if (context.defaultChangesetComment()) {
92365                     corePreferences('comment', context.defaultChangesetComment());
92366                     corePreferences('commentDate', Date.now());
92367                 }
92368                 if (context.defaultChangesetSource()) {
92369                     corePreferences('source', context.defaultChangesetSource());
92370                     corePreferences('commentDate', Date.now());
92371                 }
92372                 if (context.defaultChangesetHashtags()) {
92373                     corePreferences('hashtags', context.defaultChangesetHashtags());
92374                     corePreferences('commentDate', Date.now());
92375                 }
92376
92377                 var detected = utilDetect();
92378                 var tags = {
92379                     comment: corePreferences('comment') || '',
92380                     created_by: context.cleanTagValue('iD ' + context.version),
92381                     host: context.cleanTagValue(detected.host),
92382                     locale: context.cleanTagValue(_mainLocalizer.localeCode())
92383                 };
92384
92385                 // call findHashtags initially - this will remove stored
92386                 // hashtags if any hashtags are found in the comment - #4304
92387                 findHashtags(tags, true);
92388
92389                 var hashtags = corePreferences('hashtags');
92390                 if (hashtags) {
92391                     tags.hashtags = hashtags;
92392                 }
92393
92394                 var source = corePreferences('source');
92395                 if (source) {
92396                     tags.source = source;
92397                 }
92398                 var photoOverlaysUsed = context.history().photoOverlaysUsed();
92399                 if (photoOverlaysUsed.length) {
92400                     var sources = (tags.source || '').split(';');
92401
92402                     // include this tag for any photo layer
92403                     if (sources.indexOf('streetlevel imagery') === -1) {
92404                         sources.push('streetlevel imagery');
92405                     }
92406
92407                     // add the photo overlays used during editing as sources
92408                     photoOverlaysUsed.forEach(function(photoOverlay) {
92409                         if (sources.indexOf(photoOverlay) === -1) {
92410                             sources.push(photoOverlay);
92411                         }
92412                     });
92413
92414                     tags.source = context.cleanTagValue(sources.join(';'));
92415                 }
92416
92417                 context.changeset = new osmChangeset({ tags: tags });
92418             }
92419
92420             // Calculates read-only metadata tags based on the user's editing session and applies
92421             // them to the changeset.
92422             function loadDerivedChangesetTags() {
92423
92424                 var osm = context.connection();
92425                 if (!osm) { return; }
92426
92427                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92428
92429                 // assign tags for imagery used
92430                 var imageryUsed = context.cleanTagValue(context.history().imageryUsed().join(';'));
92431                 tags.imagery_used = imageryUsed || 'None';
92432
92433                 // assign tags for closed issues and notes
92434                 var osmClosed = osm.getClosedIDs();
92435                 var itemType;
92436                 if (osmClosed.length) {
92437                     tags['closed:note'] = context.cleanTagValue(osmClosed.join(';'));
92438                 }
92439                 if (services.keepRight) {
92440                     var krClosed = services.keepRight.getClosedIDs();
92441                     if (krClosed.length) {
92442                         tags['closed:keepright'] = context.cleanTagValue(krClosed.join(';'));
92443                     }
92444                 }
92445                 if (services.improveOSM) {
92446                     var iOsmClosed = services.improveOSM.getClosedCounts();
92447                     for (itemType in iOsmClosed) {
92448                         tags['closed:improveosm:' + itemType] = context.cleanTagValue(iOsmClosed[itemType].toString());
92449                     }
92450                 }
92451                 if (services.osmose) {
92452                     var osmoseClosed = services.osmose.getClosedCounts();
92453                     for (itemType in osmoseClosed) {
92454                         tags['closed:osmose:' + itemType] = context.cleanTagValue(osmoseClosed[itemType].toString());
92455                     }
92456                 }
92457
92458                 // remove existing issue counts
92459                 for (var key in tags) {
92460                     if (key.match(/(^warnings:)|(^resolved:)/)) {
92461                         delete tags[key];
92462                     }
92463                 }
92464
92465                 function addIssueCounts(issues, prefix) {
92466                     var issuesByType = utilArrayGroupBy(issues, 'type');
92467                     for (var issueType in issuesByType) {
92468                         var issuesOfType = issuesByType[issueType];
92469                         if (issuesOfType[0].subtype) {
92470                             var issuesBySubtype = utilArrayGroupBy(issuesOfType, 'subtype');
92471                             for (var issueSubtype in issuesBySubtype) {
92472                                 var issuesOfSubtype = issuesBySubtype[issueSubtype];
92473                                 tags[prefix + ':' + issueType + ':' + issueSubtype] = context.cleanTagValue(issuesOfSubtype.length.toString());
92474                             }
92475                         } else {
92476                             tags[prefix + ':' + issueType] = context.cleanTagValue(issuesOfType.length.toString());
92477                         }
92478                     }
92479                 }
92480
92481                 // add counts of warnings generated by the user's edits
92482                 var warnings = context.validator()
92483                     .getIssuesBySeverity({ what: 'edited', where: 'all', includeIgnored: true, includeDisabledRules: true }).warning;
92484                 addIssueCounts(warnings, 'warnings');
92485
92486                 // add counts of issues resolved by the user's edits
92487                 var resolvedIssues = context.validator().getResolvedIssues();
92488                 addIssueCounts(resolvedIssues, 'resolved');
92489
92490                 context.changeset = context.changeset.update({ tags: tags });
92491             }
92492
92493             function render(selection) {
92494
92495                 var osm = context.connection();
92496                 if (!osm) { return; }
92497
92498                 var header = selection.selectAll('.header')
92499                     .data([0]);
92500
92501                 var headerTitle = header.enter()
92502                     .append('div')
92503                     .attr('class', 'header fillL header-container');
92504
92505                 headerTitle
92506                     .append('div')
92507                     .attr('class', 'header-block header-block-outer');
92508
92509                 headerTitle
92510                     .append('div')
92511                     .attr('class', 'header-block')
92512                     .append('h3')
92513                     .text(_t('commit.title'));
92514
92515                 headerTitle
92516                     .append('div')
92517                     .attr('class', 'header-block header-block-outer header-block-close')
92518                     .append('button')
92519                     .attr('class', 'close')
92520                     .on('click', function() {
92521                         dispatch$1.call('cancel', this);
92522                     })
92523                     .call(svgIcon('#iD-icon-close'));
92524
92525                 var body = selection.selectAll('.body')
92526                     .data([0]);
92527
92528                 body = body.enter()
92529                     .append('div')
92530                     .attr('class', 'body')
92531                     .merge(body);
92532
92533
92534                 // Changeset Section
92535                 var changesetSection = body.selectAll('.changeset-editor')
92536                     .data([0]);
92537
92538                 changesetSection = changesetSection.enter()
92539                     .append('div')
92540                     .attr('class', 'modal-section changeset-editor')
92541                     .merge(changesetSection);
92542
92543                 changesetSection
92544                     .call(changesetEditor
92545                         .changesetID(context.changeset.id)
92546                         .tags(context.changeset.tags)
92547                     );
92548
92549
92550                 // Warnings
92551                 body.call(commitWarnings);
92552
92553
92554                 // Upload Explanation
92555                 var saveSection = body.selectAll('.save-section')
92556                     .data([0]);
92557
92558                 saveSection = saveSection.enter()
92559                     .append('div')
92560                     .attr('class','modal-section save-section fillL')
92561                     .merge(saveSection);
92562
92563                 var prose = saveSection.selectAll('.commit-info')
92564                     .data([0]);
92565
92566                 if (prose.enter().size()) {   // first time, make sure to update user details in prose
92567                     _userDetails = null;
92568                 }
92569
92570                 prose = prose.enter()
92571                     .append('p')
92572                     .attr('class', 'commit-info')
92573                     .text(_t('commit.upload_explanation'))
92574                     .merge(prose);
92575
92576                 // always check if this has changed, but only update prose.html()
92577                 // if needed, because it can trigger a style recalculation
92578                 osm.userDetails(function(err, user) {
92579                     if (err) { return; }
92580
92581                     if (_userDetails === user) { return; }  // no change
92582                     _userDetails = user;
92583
92584                     var userLink = select(document.createElement('div'));
92585
92586                     if (user.image_url) {
92587                         userLink
92588                             .append('img')
92589                             .attr('src', user.image_url)
92590                             .attr('class', 'icon pre-text user-icon');
92591                     }
92592
92593                     userLink
92594                         .append('a')
92595                         .attr('class', 'user-info')
92596                         .text(user.display_name)
92597                         .attr('href', osm.userURL(user.display_name))
92598                         .attr('target', '_blank');
92599
92600                     prose
92601                         .html(_t('commit.upload_explanation_with_user', { user: userLink.html() }));
92602                 });
92603
92604
92605                 // Request Review
92606                 var requestReview = saveSection.selectAll('.request-review')
92607                     .data([0]);
92608
92609                 // Enter
92610                 var requestReviewEnter = requestReview.enter()
92611                     .append('div')
92612                     .attr('class', 'request-review');
92613
92614                 var requestReviewDomId = utilUniqueDomId('commit-input-request-review');
92615
92616                 var labelEnter = requestReviewEnter
92617                     .append('label')
92618                     .attr('for', requestReviewDomId);
92619
92620                 labelEnter
92621                     .append('input')
92622                     .attr('type', 'checkbox')
92623                     .attr('id', requestReviewDomId);
92624
92625                 labelEnter
92626                     .append('span')
92627                     .text(_t('commit.request_review'));
92628
92629                 // Update
92630                 requestReview = requestReview
92631                     .merge(requestReviewEnter);
92632
92633                 var requestReviewInput = requestReview.selectAll('input')
92634                     .property('checked', isReviewRequested(context.changeset.tags))
92635                     .on('change', toggleRequestReview);
92636
92637
92638                 // Buttons
92639                 var buttonSection = saveSection.selectAll('.buttons')
92640                     .data([0]);
92641
92642                 // enter
92643                 var buttonEnter = buttonSection.enter()
92644                     .append('div')
92645                     .attr('class', 'buttons fillL');
92646
92647                 buttonEnter
92648                     .append('button')
92649                     .attr('class', 'secondary-action button cancel-button')
92650                     .append('span')
92651                     .attr('class', 'label')
92652                     .text(_t('commit.cancel'));
92653
92654                 var uploadButton = buttonEnter
92655                     .append('button')
92656                     .attr('class', 'action button save-button');
92657
92658                 uploadButton.append('span')
92659                     .attr('class', 'label')
92660                     .text(_t('commit.save'));
92661
92662                 var uploadBlockerTooltipText = getUploadBlockerMessage();
92663
92664                 // update
92665                 buttonSection = buttonSection
92666                     .merge(buttonEnter);
92667
92668                 buttonSection.selectAll('.cancel-button')
92669                     .on('click.cancel', function() {
92670                         dispatch$1.call('cancel', this);
92671                     });
92672
92673                 buttonSection.selectAll('.save-button')
92674                     .classed('disabled', uploadBlockerTooltipText !== null)
92675                     .on('click.save', function() {
92676                         if (!select(this).classed('disabled')) {
92677                             this.blur();    // avoid keeping focus on the button - #4641
92678                             context.uploader().save(context.changeset);
92679                         }
92680                     });
92681
92682                 // remove any existing tooltip
92683                 uiTooltip().destroyAny(buttonSection.selectAll('.save-button'));
92684
92685                 if (uploadBlockerTooltipText) {
92686                     buttonSection.selectAll('.save-button')
92687                         .call(uiTooltip().title(uploadBlockerTooltipText).placement('top'));
92688                 }
92689
92690                 // Raw Tag Editor
92691                 var tagSection = body.selectAll('.tag-section.raw-tag-editor')
92692                     .data([0]);
92693
92694                 tagSection = tagSection.enter()
92695                     .append('div')
92696                     .attr('class', 'modal-section tag-section raw-tag-editor')
92697                     .merge(tagSection);
92698
92699                 tagSection
92700                     .call(rawTagEditor
92701                         .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92702                         .render
92703                     );
92704
92705                 var changesSection = body.selectAll('.commit-changes-section')
92706                     .data([0]);
92707
92708                 changesSection = changesSection.enter()
92709                     .append('div')
92710                     .attr('class', 'modal-section commit-changes-section')
92711                     .merge(changesSection);
92712
92713                 // Change summary
92714                 changesSection.call(commitChanges.render);
92715
92716
92717                 function toggleRequestReview() {
92718                     var rr = requestReviewInput.property('checked');
92719                     updateChangeset({ review_requested: (rr ? 'yes' : undefined) });
92720
92721                     tagSection
92722                         .call(rawTagEditor
92723                             .tags(Object.assign({}, context.changeset.tags))   // shallow copy
92724                             .render
92725                         );
92726                 }
92727             }
92728
92729
92730             function getUploadBlockerMessage() {
92731                 var errors = context.validator()
92732                     .getIssuesBySeverity({ what: 'edited', where: 'all' }).error;
92733
92734                 if (errors.length) {
92735                     return _t('commit.outstanding_errors_message', { count: errors.length });
92736
92737                 } else {
92738                     var hasChangesetComment = context.changeset && context.changeset.tags.comment && context.changeset.tags.comment.trim().length;
92739                     if (!hasChangesetComment) {
92740                         return _t('commit.comment_needed_message');
92741                     }
92742                 }
92743                 return null;
92744             }
92745
92746
92747             function changeTags(_, changed, onInput) {
92748                 if (changed.hasOwnProperty('comment')) {
92749                     if (changed.comment === undefined) {
92750                         changed.comment = '';
92751                     }
92752                     if (!onInput) {
92753                         corePreferences('comment', changed.comment);
92754                         corePreferences('commentDate', Date.now());
92755                     }
92756                 }
92757                 if (changed.hasOwnProperty('source')) {
92758                     if (changed.source === undefined) {
92759                         corePreferences('source', null);
92760                     } else if (!onInput) {
92761                         corePreferences('source', changed.source);
92762                         corePreferences('commentDate', Date.now());
92763                     }
92764                 }
92765                 // no need to update `prefs` for `hashtags` here since it's done in `updateChangeset`
92766
92767                 updateChangeset(changed, onInput);
92768
92769                 if (_selection) {
92770                     _selection.call(render);
92771                 }
92772             }
92773
92774
92775             function findHashtags(tags, commentOnly) {
92776                 var detectedHashtags = commentHashtags();
92777
92778                 if (detectedHashtags.length) {
92779                     // always remove stored hashtags if there are hashtags in the comment - #4304
92780                     corePreferences('hashtags', null);
92781                 }
92782                 if (!detectedHashtags.length || !commentOnly) {
92783                     detectedHashtags = detectedHashtags.concat(hashtagHashtags());
92784                 }
92785
92786                 var allLowerCase = new Set();
92787                 return detectedHashtags.filter(function(hashtag) {
92788                     // Compare tags as lowercase strings, but keep original case tags
92789                     var lowerCase = hashtag.toLowerCase();
92790                     if (!allLowerCase.has(lowerCase)) {
92791                         allLowerCase.add(lowerCase);
92792                         return true;
92793                     }
92794                     return false;
92795                 });
92796
92797                 // Extract hashtags from `comment`
92798                 function commentHashtags() {
92799                     var matches = (tags.comment || '')
92800                         .replace(/http\S*/g, '')  // drop anything that looks like a URL - #4289
92801                         .match(hashtagRegex);
92802
92803                     return matches || [];
92804                 }
92805
92806                 // Extract and clean hashtags from `hashtags`
92807                 function hashtagHashtags() {
92808                     var matches = (tags.hashtags || '')
92809                         .split(/[,;\s]+/)
92810                         .map(function (s) {
92811                             if (s[0] !== '#') { s = '#' + s; }    // prepend '#'
92812                             var matched = s.match(hashtagRegex);
92813                             return matched && matched[0];
92814                         }).filter(Boolean);                       // exclude falsy
92815
92816                     return matches || [];
92817                 }
92818             }
92819
92820
92821             function isReviewRequested(tags) {
92822                 var rr = tags.review_requested;
92823                 if (rr === undefined) { return false; }
92824                 rr = rr.trim().toLowerCase();
92825                 return !(rr === '' || rr === 'no');
92826             }
92827
92828
92829             function updateChangeset(changed, onInput) {
92830                 var tags = Object.assign({}, context.changeset.tags);   // shallow copy
92831
92832                 Object.keys(changed).forEach(function(k) {
92833                     var v = changed[k];
92834                     k = context.cleanTagKey(k);
92835                     if (readOnlyTags.indexOf(k) !== -1) { return; }
92836
92837                     if (k !== '' && v !== undefined) {
92838                         if (onInput) {
92839                             tags[k] = v;
92840                         } else {
92841                             tags[k] = context.cleanTagValue(v);
92842                         }
92843                     } else {
92844                         delete tags[k];
92845                     }
92846                 });
92847
92848                 if (!onInput) {
92849                     // when changing the comment, override hashtags with any found in comment.
92850                     var commentOnly = changed.hasOwnProperty('comment') && (changed.comment !== '');
92851                     var arr = findHashtags(tags, commentOnly);
92852                     if (arr.length) {
92853                         tags.hashtags = context.cleanTagValue(arr.join(';'));
92854                         corePreferences('hashtags', tags.hashtags);
92855                     } else {
92856                         delete tags.hashtags;
92857                         corePreferences('hashtags', null);
92858                     }
92859                 }
92860
92861                 // always update userdetails, just in case user reauthenticates as someone else
92862                 if (_userDetails && _userDetails.changesets_count !== undefined) {
92863                     var changesetsCount = parseInt(_userDetails.changesets_count, 10) + 1;  // #4283
92864                     tags.changesets_count = String(changesetsCount);
92865
92866                     // first 100 edits - new user
92867                     if (changesetsCount <= 100) {
92868                         var s;
92869                         s = corePreferences('walkthrough_completed');
92870                         if (s) {
92871                             tags['ideditor:walkthrough_completed'] = s;
92872                         }
92873
92874                         s = corePreferences('walkthrough_progress');
92875                         if (s) {
92876                             tags['ideditor:walkthrough_progress'] = s;
92877                         }
92878
92879                         s = corePreferences('walkthrough_started');
92880                         if (s) {
92881                             tags['ideditor:walkthrough_started'] = s;
92882                         }
92883                     }
92884                 } else {
92885                     delete tags.changesets_count;
92886                 }
92887
92888                 if (!fastDeepEqual(context.changeset.tags, tags)) {
92889                     context.changeset = context.changeset.update({ tags: tags });
92890                 }
92891             }
92892
92893
92894             commit.reset = function() {
92895                 context.changeset = null;
92896             };
92897
92898
92899             return utilRebind(commit, dispatch$1, 'on');
92900         }
92901
92902         var RADIUS = 6378137;
92903         var FLATTENING = 1/298.257223563;
92904         var POLAR_RADIUS$1 = 6356752.3142;
92905
92906         var wgs84 = {
92907                 RADIUS: RADIUS,
92908                 FLATTENING: FLATTENING,
92909                 POLAR_RADIUS: POLAR_RADIUS$1
92910         };
92911
92912         var geometry_1 = geometry;
92913         var ring = ringArea;
92914
92915         function geometry(_) {
92916             var area = 0, i;
92917             switch (_.type) {
92918                 case 'Polygon':
92919                     return polygonArea(_.coordinates);
92920                 case 'MultiPolygon':
92921                     for (i = 0; i < _.coordinates.length; i++) {
92922                         area += polygonArea(_.coordinates[i]);
92923                     }
92924                     return area;
92925                 case 'Point':
92926                 case 'MultiPoint':
92927                 case 'LineString':
92928                 case 'MultiLineString':
92929                     return 0;
92930                 case 'GeometryCollection':
92931                     for (i = 0; i < _.geometries.length; i++) {
92932                         area += geometry(_.geometries[i]);
92933                     }
92934                     return area;
92935             }
92936         }
92937
92938         function polygonArea(coords) {
92939             var area = 0;
92940             if (coords && coords.length > 0) {
92941                 area += Math.abs(ringArea(coords[0]));
92942                 for (var i = 1; i < coords.length; i++) {
92943                     area -= Math.abs(ringArea(coords[i]));
92944                 }
92945             }
92946             return area;
92947         }
92948
92949         /**
92950          * Calculate the approximate area of the polygon were it projected onto
92951          *     the earth.  Note that this area will be positive if ring is oriented
92952          *     clockwise, otherwise it will be negative.
92953          *
92954          * Reference:
92955          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
92956          *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
92957          *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
92958          *
92959          * Returns:
92960          * {float} The approximate signed geodesic area of the polygon in square
92961          *     meters.
92962          */
92963
92964         function ringArea(coords) {
92965             var p1, p2, p3, lowerIndex, middleIndex, upperIndex, i,
92966             area = 0,
92967             coordsLength = coords.length;
92968
92969             if (coordsLength > 2) {
92970                 for (i = 0; i < coordsLength; i++) {
92971                     if (i === coordsLength - 2) {// i = N-2
92972                         lowerIndex = coordsLength - 2;
92973                         middleIndex = coordsLength -1;
92974                         upperIndex = 0;
92975                     } else if (i === coordsLength - 1) {// i = N-1
92976                         lowerIndex = coordsLength - 1;
92977                         middleIndex = 0;
92978                         upperIndex = 1;
92979                     } else { // i = 0 to N-3
92980                         lowerIndex = i;
92981                         middleIndex = i+1;
92982                         upperIndex = i+2;
92983                     }
92984                     p1 = coords[lowerIndex];
92985                     p2 = coords[middleIndex];
92986                     p3 = coords[upperIndex];
92987                     area += ( rad(p3[0]) - rad(p1[0]) ) * Math.sin( rad(p2[1]));
92988                 }
92989
92990                 area = area * wgs84.RADIUS * wgs84.RADIUS / 2;
92991             }
92992
92993             return area;
92994         }
92995
92996         function rad(_) {
92997             return _ * Math.PI / 180;
92998         }
92999
93000         var geojsonArea = {
93001                 geometry: geometry_1,
93002                 ring: ring
93003         };
93004
93005         function toRadians(angleInDegrees) {
93006           return (angleInDegrees * Math.PI) / 180;
93007         }
93008
93009         function toDegrees(angleInRadians) {
93010           return (angleInRadians * 180) / Math.PI;
93011         }
93012
93013         function offset(c1, distance, bearing) {
93014           var lat1 = toRadians(c1[1]);
93015           var lon1 = toRadians(c1[0]);
93016           var dByR = distance / 6378137; // distance divided by 6378137 (radius of the earth) wgs84
93017           var lat = Math.asin(
93018             Math.sin(lat1) * Math.cos(dByR) +
93019               Math.cos(lat1) * Math.sin(dByR) * Math.cos(bearing)
93020           );
93021           var lon =
93022             lon1 +
93023             Math.atan2(
93024               Math.sin(bearing) * Math.sin(dByR) * Math.cos(lat1),
93025               Math.cos(dByR) - Math.sin(lat1) * Math.sin(lat)
93026             );
93027           return [toDegrees(lon), toDegrees(lat)];
93028         }
93029
93030         function validateCenter(center) {
93031           var validCenterLengths = [2, 3];
93032           if (!Array.isArray(center) || !validCenterLengths.includes(center.length)) {
93033             throw new Error("ERROR! Center has to be an array of length two or three");
93034           }
93035           var lng = center[0];
93036           var lat = center[1];
93037           if (typeof lng !== "number" || typeof lat !== "number") {
93038             throw new Error(
93039               ("ERROR! Longitude and Latitude has to be numbers but where " + (typeof lng) + " and " + (typeof lat))
93040             );
93041           }
93042           if (lng > 180 || lng < -180) {
93043             throw new Error(
93044               ("ERROR! Longitude has to be between -180 and 180 but was " + lng)
93045             );
93046           }
93047
93048           if (lat > 90 || lat < -90) {
93049             throw new Error(
93050               ("ERROR! Latitude has to be between -90 and 90 but was " + lat)
93051             );
93052           }
93053         }
93054
93055         function validateRadius(radius) {
93056           if (typeof radius !== "number") {
93057             throw new Error(
93058               ("ERROR! Radius has to be a positive number but was: " + (typeof radius))
93059             );
93060           }
93061
93062           if (radius <= 0) {
93063             throw new Error(
93064               ("ERROR! Radius has to be a positive number but was: " + radius)
93065             );
93066           }
93067         }
93068
93069         function validateNumberOfSegments(numberOfSegments) {
93070           if (typeof numberOfSegments !== "number" && numberOfSegments !== undefined) {
93071             throw new Error(
93072               ("ERROR! Number of segments has to be a number but was: " + (typeof numberOfSegments))
93073             );
93074           }
93075
93076           if (numberOfSegments < 3) {
93077             throw new Error(
93078               ("ERROR! Number of segments has to be at least 3 but was: " + numberOfSegments)
93079             );
93080           }
93081         }
93082
93083         function validateInput(ref) {
93084           var center = ref.center;
93085           var radius = ref.radius;
93086           var numberOfSegments = ref.numberOfSegments;
93087
93088           validateCenter(center);
93089           validateRadius(radius);
93090           validateNumberOfSegments(numberOfSegments);
93091         }
93092
93093         var circleToPolygon = function circleToPolygon(center, radius, numberOfSegments) {
93094           var n = numberOfSegments ? numberOfSegments : 32;
93095
93096           // validateInput() throws error on invalid input and do nothing on valid input
93097           validateInput({ center: center, radius: radius, numberOfSegments: numberOfSegments });
93098
93099           var coordinates = [];
93100           for (var i = 0; i < n; ++i) {
93101             coordinates.push(offset(center, radius, (2 * Math.PI * -i) / n));
93102           }
93103           coordinates.push(coordinates[0]);
93104
93105           return {
93106             type: "Polygon",
93107             coordinates: [coordinates]
93108           };
93109         };
93110
93111         var geojsonPrecision = createCommonjsModule(function (module) {
93112         (function() {
93113
93114           function parse(t, coordinatePrecision, extrasPrecision) {
93115
93116             function point(p) {
93117               return p.map(function(e, index) {
93118                 if (index < 2) {
93119                     return 1 * e.toFixed(coordinatePrecision);
93120                 } else {
93121                     return 1 * e.toFixed(extrasPrecision);
93122                 }
93123               });
93124             }
93125
93126             function multi(l) {
93127               return l.map(point);
93128             }
93129
93130             function poly(p) {
93131               return p.map(multi);
93132             }
93133
93134             function multiPoly(m) {
93135               return m.map(poly);
93136             }
93137
93138             function geometry(obj) {
93139               if (!obj) {
93140                 return {};
93141               }
93142               
93143               switch (obj.type) {
93144                 case "Point":
93145                   obj.coordinates = point(obj.coordinates);
93146                   return obj;
93147                 case "LineString":
93148                 case "MultiPoint":
93149                   obj.coordinates = multi(obj.coordinates);
93150                   return obj;
93151                 case "Polygon":
93152                 case "MultiLineString":
93153                   obj.coordinates = poly(obj.coordinates);
93154                   return obj;
93155                 case "MultiPolygon":
93156                   obj.coordinates = multiPoly(obj.coordinates);
93157                   return obj;
93158                 case "GeometryCollection":
93159                   obj.geometries = obj.geometries.map(geometry);
93160                   return obj;
93161                 default :
93162                   return {};
93163               }
93164             }
93165
93166             function feature(obj) {
93167               obj.geometry = geometry(obj.geometry);
93168               return obj
93169             }
93170
93171             function featureCollection(f) {
93172               f.features = f.features.map(feature);
93173               return f;
93174             }
93175
93176             function geometryCollection(g) {
93177               g.geometries = g.geometries.map(geometry);
93178               return g;
93179             }
93180
93181             if (!t) {
93182               return t;
93183             }
93184
93185             switch (t.type) {
93186               case "Feature":
93187                 return feature(t);
93188               case "GeometryCollection" :
93189                 return geometryCollection(t);
93190               case "FeatureCollection" :
93191                 return featureCollection(t);
93192               case "Point":
93193               case "LineString":
93194               case "Polygon":
93195               case "MultiPoint":
93196               case "MultiPolygon":
93197               case "MultiLineString":
93198                 return geometry(t);
93199               default :
93200                 return t;
93201             }
93202               
93203           }
93204
93205           module.exports = parse;
93206           module.exports.parse = parse;
93207
93208         }());
93209         });
93210
93211         /* Polyfill service v3.13.0
93212          * For detailed credits and licence information see http://github.com/financial-times/polyfill-service
93213          *
93214          * - Array.prototype.fill, License: CC0 */
93215
93216         if (!('fill' in Array.prototype)) {
93217           Object.defineProperty(Array.prototype, 'fill', {
93218             configurable: true,
93219             value: function fill (value) {
93220               if (this === undefined || this === null) {
93221                 throw new TypeError(this + ' is not an object')
93222               }
93223
93224               var arrayLike = Object(this);
93225
93226               var length = Math.max(Math.min(arrayLike.length, 9007199254740991), 0) || 0;
93227
93228               var relativeStart = 1 in arguments ? parseInt(Number(arguments[1]), 10) || 0 : 0;
93229
93230               relativeStart = relativeStart < 0 ? Math.max(length + relativeStart, 0) : Math.min(relativeStart, length);
93231
93232               var relativeEnd = 2 in arguments && arguments[2] !== undefined ? parseInt(Number(arguments[2]), 10) || 0 : length;
93233
93234               relativeEnd = relativeEnd < 0 ? Math.max(length + arguments[2], 0) : Math.min(relativeEnd, length);
93235
93236               while (relativeStart < relativeEnd) {
93237                 arrayLike[relativeStart] = value;
93238
93239                 ++relativeStart;
93240               }
93241
93242               return arrayLike
93243             },
93244             writable: true
93245           });
93246         }
93247
93248         /**
93249          * Polyfill for IE support
93250          */
93251         Number.isFinite = Number.isFinite || function (value) {
93252           return typeof value === 'number' && isFinite(value)
93253         };
93254
93255         Number.isInteger = Number.isInteger || function (val) {
93256           return typeof val === 'number' &&
93257           isFinite(val) &&
93258           Math.floor(val) === val
93259         };
93260
93261         Number.parseFloat = Number.parseFloat || parseFloat;
93262
93263         Number.isNaN = Number.isNaN || function (value) {
93264           return value !== value // eslint-disable-line
93265         };
93266
93267         /**
93268          * Polyfill for IE support
93269          */
93270         Math.trunc = Math.trunc || function (x) {
93271           return x < 0 ? Math.ceil(x) : Math.floor(x)
93272         };
93273
93274         var NumberUtil = function NumberUtil () {};
93275
93276         NumberUtil.prototype.interfaces_ = function interfaces_ () {
93277           return []
93278         };
93279         NumberUtil.prototype.getClass = function getClass () {
93280           return NumberUtil
93281         };
93282         NumberUtil.prototype.equalsWithTolerance = function equalsWithTolerance (x1, x2, tolerance) {
93283           return Math.abs(x1 - x2) <= tolerance
93284         };
93285
93286         var IllegalArgumentException = (function (Error) {
93287                 function IllegalArgumentException (message) {
93288                         Error.call(this, message);
93289                         this.name = 'IllegalArgumentException';
93290                         this.message = message;
93291                         this.stack = (new Error()).stack;
93292                 }
93293
93294                 if ( Error ) { IllegalArgumentException.__proto__ = Error; }
93295                 IllegalArgumentException.prototype = Object.create( Error && Error.prototype );
93296                 IllegalArgumentException.prototype.constructor = IllegalArgumentException;
93297
93298                 return IllegalArgumentException;
93299         }(Error));
93300
93301         var Double = function Double () {};
93302
93303         var staticAccessors$1 = { MAX_VALUE: { configurable: true } };
93304
93305         Double.isNaN = function isNaN (n) { return Number.isNaN(n) };
93306         Double.doubleToLongBits = function doubleToLongBits (n) { return n };
93307         Double.longBitsToDouble = function longBitsToDouble (n) { return n };
93308         Double.isInfinite = function isInfinite (n) { return !Number.isFinite(n) };
93309         staticAccessors$1.MAX_VALUE.get = function () { return Number.MAX_VALUE };
93310
93311         Object.defineProperties( Double, staticAccessors$1 );
93312
93313         var Comparable = function Comparable () {};
93314
93315         var Clonable = function Clonable () {};
93316
93317         var Comparator = function Comparator () {};
93318
93319         function Serializable () {}
93320
93321         // import Assert from '../util/Assert'
93322
93323         var Coordinate = function Coordinate () {
93324           this.x = null;
93325           this.y = null;
93326           this.z = null;
93327           if (arguments.length === 0) {
93328             this.x = 0.0;
93329             this.y = 0.0;
93330             this.z = Coordinate.NULL_ORDINATE;
93331           } else if (arguments.length === 1) {
93332             var c = arguments[0];
93333             this.x = c.x;
93334             this.y = c.y;
93335             this.z = c.z;
93336           } else if (arguments.length === 2) {
93337             this.x = arguments[0];
93338             this.y = arguments[1];
93339             this.z = Coordinate.NULL_ORDINATE;
93340           } else if (arguments.length === 3) {
93341             this.x = arguments[0];
93342             this.y = arguments[1];
93343             this.z = arguments[2];
93344           }
93345         };
93346
93347         var staticAccessors = { DimensionalComparator: { configurable: true },serialVersionUID: { configurable: true },NULL_ORDINATE: { configurable: true },X: { configurable: true },Y: { configurable: true },Z: { configurable: true } };
93348         Coordinate.prototype.setOrdinate = function setOrdinate (ordinateIndex, value) {
93349           switch (ordinateIndex) {
93350             case Coordinate.X:
93351               this.x = value;
93352               break
93353             case Coordinate.Y:
93354               this.y = value;
93355               break
93356             case Coordinate.Z:
93357               this.z = value;
93358               break
93359             default:
93360               throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93361           }
93362         };
93363         Coordinate.prototype.equals2D = function equals2D () {
93364           if (arguments.length === 1) {
93365             var other = arguments[0];
93366             if (this.x !== other.x) {
93367               return false
93368             }
93369             if (this.y !== other.y) {
93370               return false
93371             }
93372             return true
93373           } else if (arguments.length === 2) {
93374             var c = arguments[0];
93375             var tolerance = arguments[1];
93376             if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) {
93377               return false
93378             }
93379             if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) {
93380               return false
93381             }
93382             return true
93383           }
93384         };
93385         Coordinate.prototype.getOrdinate = function getOrdinate (ordinateIndex) {
93386           switch (ordinateIndex) {
93387             case Coordinate.X:
93388               return this.x
93389             case Coordinate.Y:
93390               return this.y
93391             case Coordinate.Z:
93392               return this.z
93393           }
93394           throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex)
93395         };
93396         Coordinate.prototype.equals3D = function equals3D (other) {
93397           return this.x === other.x &&
93398                  this.y === other.y &&
93399                  ((this.z === other.z || Double.isNaN(this.z)) &&
93400                  Double.isNaN(other.z))
93401         };
93402         Coordinate.prototype.equals = function equals (other) {
93403           if (!(other instanceof Coordinate)) {
93404             return false
93405           }
93406           return this.equals2D(other)
93407         };
93408         Coordinate.prototype.equalInZ = function equalInZ (c, tolerance) {
93409           return NumberUtil.equalsWithTolerance(this.z, c.z, tolerance)
93410         };
93411         Coordinate.prototype.compareTo = function compareTo (o) {
93412           var other = o;
93413           if (this.x < other.x) { return -1 }
93414           if (this.x > other.x) { return 1 }
93415           if (this.y < other.y) { return -1 }
93416           if (this.y > other.y) { return 1 }
93417           return 0
93418         };
93419         Coordinate.prototype.clone = function clone () {
93420           // try {
93421           // var coord = null
93422           // return coord
93423           // } catch (e) {
93424           // if (e instanceof CloneNotSupportedException) {
93425           //   Assert.shouldNeverReachHere("this shouldn't happen because this class is Cloneable")
93426           //   return null
93427           // } else throw e
93428           // } finally {}
93429         };
93430         Coordinate.prototype.copy = function copy () {
93431           return new Coordinate(this)
93432         };
93433         Coordinate.prototype.toString = function toString () {
93434           return '(' + this.x + ', ' + this.y + ', ' + this.z + ')'
93435         };
93436         Coordinate.prototype.distance3D = function distance3D (c) {
93437           var dx = this.x - c.x;
93438           var dy = this.y - c.y;
93439           var dz = this.z - c.z;
93440           return Math.sqrt(dx * dx + dy * dy + dz * dz)
93441         };
93442         Coordinate.prototype.distance = function distance (c) {
93443           var dx = this.x - c.x;
93444           var dy = this.y - c.y;
93445           return Math.sqrt(dx * dx + dy * dy)
93446         };
93447         Coordinate.prototype.hashCode = function hashCode () {
93448           var result = 17;
93449           result = 37 * result + Coordinate.hashCode(this.x);
93450           result = 37 * result + Coordinate.hashCode(this.y);
93451           return result
93452         };
93453         Coordinate.prototype.setCoordinate = function setCoordinate (other) {
93454           this.x = other.x;
93455           this.y = other.y;
93456           this.z = other.z;
93457         };
93458         Coordinate.prototype.interfaces_ = function interfaces_ () {
93459           return [Comparable, Clonable, Serializable]
93460         };
93461         Coordinate.prototype.getClass = function getClass () {
93462           return Coordinate
93463         };
93464         Coordinate.hashCode = function hashCode () {
93465           if (arguments.length === 1) {
93466             var x = arguments[0];
93467             var f = Double.doubleToLongBits(x);
93468             return Math.trunc((f ^ f) >>> 32)
93469           }
93470         };
93471         staticAccessors.DimensionalComparator.get = function () { return DimensionalComparator };
93472         staticAccessors.serialVersionUID.get = function () { return 6683108902428366910 };
93473         staticAccessors.NULL_ORDINATE.get = function () { return Double.NaN };
93474         staticAccessors.X.get = function () { return 0 };
93475         staticAccessors.Y.get = function () { return 1 };
93476         staticAccessors.Z.get = function () { return 2 };
93477
93478         Object.defineProperties( Coordinate, staticAccessors );
93479
93480         var DimensionalComparator = function DimensionalComparator (dimensionsToTest) {
93481           this._dimensionsToTest = 2;
93482           if (arguments.length === 0) ; else if (arguments.length === 1) {
93483             var dimensionsToTest$1 = arguments[0];
93484             if (dimensionsToTest$1 !== 2 && dimensionsToTest$1 !== 3) { throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') }
93485             this._dimensionsToTest = dimensionsToTest$1;
93486           }
93487         };
93488         DimensionalComparator.prototype.compare = function compare (o1, o2) {
93489           var c1 = o1;
93490           var c2 = o2;
93491           var compX = DimensionalComparator.compare(c1.x, c2.x);
93492           if (compX !== 0) { return compX }
93493           var compY = DimensionalComparator.compare(c1.y, c2.y);
93494           if (compY !== 0) { return compY }
93495           if (this._dimensionsToTest <= 2) { return 0 }
93496           var compZ = DimensionalComparator.compare(c1.z, c2.z);
93497           return compZ
93498         };
93499         DimensionalComparator.prototype.interfaces_ = function interfaces_ () {
93500           return [Comparator]
93501         };
93502         DimensionalComparator.prototype.getClass = function getClass () {
93503           return DimensionalComparator
93504         };
93505         DimensionalComparator.compare = function compare (a, b) {
93506           if (a < b) { return -1 }
93507           if (a > b) { return 1 }
93508           if (Double.isNaN(a)) {
93509             if (Double.isNaN(b)) { return 0 }
93510             return -1
93511           }
93512           if (Double.isNaN(b)) { return 1 }
93513           return 0
93514         };
93515
93516         // import hasInterface from '../../../../hasInterface'
93517         // import CoordinateSequence from './CoordinateSequence'
93518
93519         var CoordinateSequenceFactory = function CoordinateSequenceFactory () {};
93520
93521         CoordinateSequenceFactory.prototype.create = function create () {
93522           // if (arguments.length === 1) {
93523           // if (arguments[0] instanceof Array) {
93524           //   let coordinates = arguments[0]
93525           // } else if (hasInterface(arguments[0], CoordinateSequence)) {
93526           //   let coordSeq = arguments[0]
93527           // }
93528           // } else if (arguments.length === 2) {
93529           // let size = arguments[0]
93530           // let dimension = arguments[1]
93531           // }
93532         };
93533         CoordinateSequenceFactory.prototype.interfaces_ = function interfaces_ () {
93534           return []
93535         };
93536         CoordinateSequenceFactory.prototype.getClass = function getClass () {
93537           return CoordinateSequenceFactory
93538         };
93539
93540         var Location = function Location () {};
93541
93542         var staticAccessors$4 = { INTERIOR: { configurable: true },BOUNDARY: { configurable: true },EXTERIOR: { configurable: true },NONE: { configurable: true } };
93543
93544         Location.prototype.interfaces_ = function interfaces_ () {
93545           return []
93546         };
93547         Location.prototype.getClass = function getClass () {
93548           return Location
93549         };
93550         Location.toLocationSymbol = function toLocationSymbol (locationValue) {
93551           switch (locationValue) {
93552             case Location.EXTERIOR:
93553               return 'e'
93554             case Location.BOUNDARY:
93555               return 'b'
93556             case Location.INTERIOR:
93557               return 'i'
93558             case Location.NONE:
93559               return '-'
93560           }
93561           throw new IllegalArgumentException('Unknown location value: ' + locationValue)
93562         };
93563         staticAccessors$4.INTERIOR.get = function () { return 0 };
93564         staticAccessors$4.BOUNDARY.get = function () { return 1 };
93565         staticAccessors$4.EXTERIOR.get = function () { return 2 };
93566         staticAccessors$4.NONE.get = function () { return -1 };
93567
93568         Object.defineProperties( Location, staticAccessors$4 );
93569
93570         var hasInterface = function (o, i) {
93571           return o.interfaces_ && o.interfaces_().indexOf(i) > -1
93572         };
93573
93574         var MathUtil = function MathUtil () {};
93575
93576         var staticAccessors$5 = { LOG_10: { configurable: true } };
93577
93578         MathUtil.prototype.interfaces_ = function interfaces_ () {
93579           return []
93580         };
93581         MathUtil.prototype.getClass = function getClass () {
93582           return MathUtil
93583         };
93584         MathUtil.log10 = function log10 (x) {
93585           var ln = Math.log(x);
93586           if (Double.isInfinite(ln)) { return ln }
93587           if (Double.isNaN(ln)) { return ln }
93588           return ln / MathUtil.LOG_10
93589         };
93590         MathUtil.min = function min (v1, v2, v3, v4) {
93591           var min = v1;
93592           if (v2 < min) { min = v2; }
93593           if (v3 < min) { min = v3; }
93594           if (v4 < min) { min = v4; }
93595           return min
93596         };
93597         MathUtil.clamp = function clamp () {
93598           if (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number')) {
93599             var x = arguments[0];
93600             var min = arguments[1];
93601             var max = arguments[2];
93602             if (x < min) { return min }
93603             if (x > max) { return max }
93604             return x
93605           } else if (Number.isInteger(arguments[2]) && (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1]))) {
93606             var x$1 = arguments[0];
93607             var min$1 = arguments[1];
93608             var max$1 = arguments[2];
93609             if (x$1 < min$1) { return min$1 }
93610             if (x$1 > max$1) { return max$1 }
93611             return x$1
93612           }
93613         };
93614         MathUtil.wrap = function wrap (index, max) {
93615           if (index < 0) {
93616             return max - -index % max
93617           }
93618           return index % max
93619         };
93620         MathUtil.max = function max () {
93621           if (arguments.length === 3) {
93622             var v1 = arguments[0];
93623             var v2 = arguments[1];
93624             var v3 = arguments[2];
93625             var max = v1;
93626             if (v2 > max) { max = v2; }
93627             if (v3 > max) { max = v3; }
93628             return max
93629           } else if (arguments.length === 4) {
93630             var v1$1 = arguments[0];
93631             var v2$1 = arguments[1];
93632             var v3$1 = arguments[2];
93633             var v4 = arguments[3];
93634             var max$1 = v1$1;
93635             if (v2$1 > max$1) { max$1 = v2$1; }
93636             if (v3$1 > max$1) { max$1 = v3$1; }
93637             if (v4 > max$1) { max$1 = v4; }
93638             return max$1
93639           }
93640         };
93641         MathUtil.average = function average (x1, x2) {
93642           return (x1 + x2) / 2.0
93643         };
93644         staticAccessors$5.LOG_10.get = function () { return Math.log(10) };
93645
93646         Object.defineProperties( MathUtil, staticAccessors$5 );
93647
93648         var StringBuffer = function StringBuffer (str) {
93649           this.str = str;
93650         };
93651         StringBuffer.prototype.append = function append (e) {
93652           this.str += e;
93653         };
93654
93655         StringBuffer.prototype.setCharAt = function setCharAt (i, c) {
93656           this.str = this.str.substr(0, i) + c + this.str.substr(i + 1);
93657         };
93658
93659         StringBuffer.prototype.toString = function toString (e) {
93660           return this.str
93661         };
93662
93663         var Integer = function Integer (value) {
93664           this.value = value;
93665         };
93666         Integer.prototype.intValue = function intValue () {
93667           return this.value
93668         };
93669         Integer.prototype.compareTo = function compareTo (o) {
93670           if (this.value < o) { return -1 }
93671           if (this.value > o) { return 1 }
93672           return 0
93673         };
93674         Integer.isNaN = function isNaN (n) { return Number.isNaN(n) };
93675
93676         var Character = function Character () {};
93677
93678         Character.isWhitespace = function isWhitespace (c) { return ((c <= 32 && c >= 0) || c === 127) };
93679         Character.toUpperCase = function toUpperCase (c) { return c.toUpperCase() };
93680
93681         var DD = function DD () {
93682           this._hi = 0.0;
93683           this._lo = 0.0;
93684           if (arguments.length === 0) {
93685             this.init(0.0);
93686           } else if (arguments.length === 1) {
93687             if (typeof arguments[0] === 'number') {
93688               var x = arguments[0];
93689               this.init(x);
93690             } else if (arguments[0] instanceof DD) {
93691               var dd = arguments[0];
93692               this.init(dd);
93693             } else if (typeof arguments[0] === 'string') {
93694               var str = arguments[0];
93695               DD.call(this, DD.parse(str));
93696             }
93697           } else if (arguments.length === 2) {
93698             var hi = arguments[0];
93699             var lo = arguments[1];
93700             this.init(hi, lo);
93701           }
93702         };
93703
93704         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 } };
93705         DD.prototype.le = function le (y) {
93706           return (this._hi < y._hi || this._hi === y._hi) && this._lo <= y._lo
93707         };
93708         DD.prototype.extractSignificantDigits = function extractSignificantDigits (insertDecimalPoint, magnitude) {
93709           var y = this.abs();
93710           var mag = DD.magnitude(y._hi);
93711           var scale = DD.TEN.pow(mag);
93712           y = y.divide(scale);
93713           if (y.gt(DD.TEN)) {
93714             y = y.divide(DD.TEN);
93715             mag += 1;
93716           } else if (y.lt(DD.ONE)) {
93717             y = y.multiply(DD.TEN);
93718             mag -= 1;
93719           }
93720           var decimalPointPos = mag + 1;
93721           var buf = new StringBuffer();
93722           var numDigits = DD.MAX_PRINT_DIGITS - 1;
93723           for (var i = 0; i <= numDigits; i++) {
93724             if (insertDecimalPoint && i === decimalPointPos) {
93725               buf.append('.');
93726             }
93727             var digit = Math.trunc(y._hi);
93728             if (digit < 0) {
93729               break
93730             }
93731             var rebiasBy10 = false;
93732             var digitChar = 0;
93733             if (digit > 9) {
93734               rebiasBy10 = true;
93735               digitChar = '9';
93736             } else {
93737               digitChar = '0' + digit;
93738             }
93739             buf.append(digitChar);
93740             y = y.subtract(DD.valueOf(digit)).multiply(DD.TEN);
93741             if (rebiasBy10) { y.selfAdd(DD.TEN); }
93742             var continueExtractingDigits = true;
93743             var remMag = DD.magnitude(y._hi);
93744             if (remMag < 0 && Math.abs(remMag) >= numDigits - i) { continueExtractingDigits = false; }
93745             if (!continueExtractingDigits) { break }
93746           }
93747           magnitude[0] = mag;
93748           return buf.toString()
93749         };
93750         DD.prototype.sqr = function sqr () {
93751           return this.multiply(this)
93752         };
93753         DD.prototype.doubleValue = function doubleValue () {
93754           return this._hi + this._lo
93755         };
93756         DD.prototype.subtract = function subtract () {
93757           if (arguments[0] instanceof DD) {
93758             var y = arguments[0];
93759             return this.add(y.negate())
93760           } else if (typeof arguments[0] === 'number') {
93761             var y$1 = arguments[0];
93762             return this.add(-y$1)
93763           }
93764         };
93765         DD.prototype.equals = function equals () {
93766           if (arguments.length === 1) {
93767             var y = arguments[0];
93768             return this._hi === y._hi && this._lo === y._lo
93769           }
93770         };
93771         DD.prototype.isZero = function isZero () {
93772           return this._hi === 0.0 && this._lo === 0.0
93773         };
93774         DD.prototype.selfSubtract = function selfSubtract () {
93775           if (arguments[0] instanceof DD) {
93776             var y = arguments[0];
93777             if (this.isNaN()) { return this }
93778             return this.selfAdd(-y._hi, -y._lo)
93779           } else if (typeof arguments[0] === 'number') {
93780             var y$1 = arguments[0];
93781             if (this.isNaN()) { return this }
93782             return this.selfAdd(-y$1, 0.0)
93783           }
93784         };
93785         DD.prototype.getSpecialNumberString = function getSpecialNumberString () {
93786           if (this.isZero()) { return '0.0' }
93787           if (this.isNaN()) { return 'NaN ' }
93788           return null
93789         };
93790         DD.prototype.min = function min (x) {
93791           if (this.le(x)) {
93792             return this
93793           } else {
93794             return x
93795           }
93796         };
93797         DD.prototype.selfDivide = function selfDivide () {
93798           if (arguments.length === 1) {
93799             if (arguments[0] instanceof DD) {
93800               var y = arguments[0];
93801               return this.selfDivide(y._hi, y._lo)
93802             } else if (typeof arguments[0] === 'number') {
93803               var y$1 = arguments[0];
93804               return this.selfDivide(y$1, 0.0)
93805             }
93806           } else if (arguments.length === 2) {
93807             var yhi = arguments[0];
93808             var ylo = arguments[1];
93809             var hc = null;
93810             var tc = null;
93811             var hy = null;
93812             var ty = null;
93813             var C = null;
93814             var c = null;
93815             var U = null;
93816             var u = null;
93817             C = this._hi / yhi;
93818             c = DD.SPLIT * C;
93819             hc = c - C;
93820             u = DD.SPLIT * yhi;
93821             hc = c - hc;
93822             tc = C - hc;
93823             hy = u - yhi;
93824             U = C * yhi;
93825             hy = u - hy;
93826             ty = yhi - hy;
93827             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93828             c = (this._hi - U - u + this._lo - C * ylo) / yhi;
93829             u = C + c;
93830             this._hi = u;
93831             this._lo = C - u + c;
93832             return this
93833           }
93834         };
93835         DD.prototype.dump = function dump () {
93836           return 'DD<' + this._hi + ', ' + this._lo + '>'
93837         };
93838         DD.prototype.divide = function divide () {
93839           if (arguments[0] instanceof DD) {
93840             var y = arguments[0];
93841             var hc = null;
93842             var tc = null;
93843             var hy = null;
93844             var ty = null;
93845             var C = null;
93846             var c = null;
93847             var U = null;
93848             var u = null;
93849             C = this._hi / y._hi;
93850             c = DD.SPLIT * C;
93851             hc = c - C;
93852             u = DD.SPLIT * y._hi;
93853             hc = c - hc;
93854             tc = C - hc;
93855             hy = u - y._hi;
93856             U = C * y._hi;
93857             hy = u - hy;
93858             ty = y._hi - hy;
93859             u = hc * hy - U + hc * ty + tc * hy + tc * ty;
93860             c = (this._hi - U - u + this._lo - C * y._lo) / y._hi;
93861             u = C + c;
93862             var zhi = u;
93863             var zlo = C - u + c;
93864             return new DD(zhi, zlo)
93865           } else if (typeof arguments[0] === 'number') {
93866             var y$1 = arguments[0];
93867             if (Double.isNaN(y$1)) { return DD.createNaN() }
93868             return DD.copy(this).selfDivide(y$1, 0.0)
93869           }
93870         };
93871         DD.prototype.ge = function ge (y) {
93872           return (this._hi > y._hi || this._hi === y._hi) && this._lo >= y._lo
93873         };
93874         DD.prototype.pow = function pow (exp) {
93875           if (exp === 0.0) { return DD.valueOf(1.0) }
93876           var r = new DD(this);
93877           var s = DD.valueOf(1.0);
93878           var n = Math.abs(exp);
93879           if (n > 1) {
93880             while (n > 0) {
93881               if (n % 2 === 1) {
93882                 s.selfMultiply(r);
93883               }
93884               n /= 2;
93885               if (n > 0) { r = r.sqr(); }
93886             }
93887           } else {
93888             s = r;
93889           }
93890           if (exp < 0) { return s.reciprocal() }
93891           return s
93892         };
93893         DD.prototype.ceil = function ceil () {
93894           if (this.isNaN()) { return DD.NaN }
93895           var fhi = Math.ceil(this._hi);
93896           var flo = 0.0;
93897           if (fhi === this._hi) {
93898             flo = Math.ceil(this._lo);
93899           }
93900           return new DD(fhi, flo)
93901         };
93902         DD.prototype.compareTo = function compareTo (o) {
93903           var other = o;
93904           if (this._hi < other._hi) { return -1 }
93905           if (this._hi > other._hi) { return 1 }
93906           if (this._lo < other._lo) { return -1 }
93907           if (this._lo > other._lo) { return 1 }
93908           return 0
93909         };
93910         DD.prototype.rint = function rint () {
93911           if (this.isNaN()) { return this }
93912           var plus5 = this.add(0.5);
93913           return plus5.floor()
93914         };
93915         DD.prototype.setValue = function setValue () {
93916           if (arguments[0] instanceof DD) {
93917             var value = arguments[0];
93918             this.init(value);
93919             return this
93920           } else if (typeof arguments[0] === 'number') {
93921             var value$1 = arguments[0];
93922             this.init(value$1);
93923             return this
93924           }
93925         };
93926         DD.prototype.max = function max (x) {
93927           if (this.ge(x)) {
93928             return this
93929           } else {
93930             return x
93931           }
93932         };
93933         DD.prototype.sqrt = function sqrt () {
93934           if (this.isZero()) { return DD.valueOf(0.0) }
93935           if (this.isNegative()) {
93936             return DD.NaN
93937           }
93938           var x = 1.0 / Math.sqrt(this._hi);
93939           var ax = this._hi * x;
93940           var axdd = DD.valueOf(ax);
93941           var diffSq = this.subtract(axdd.sqr());
93942           var d2 = diffSq._hi * (x * 0.5);
93943           return axdd.add(d2)
93944         };
93945         DD.prototype.selfAdd = function selfAdd () {
93946           if (arguments.length === 1) {
93947             if (arguments[0] instanceof DD) {
93948               var y = arguments[0];
93949               return this.selfAdd(y._hi, y._lo)
93950             } else if (typeof arguments[0] === 'number') {
93951               var y$1 = arguments[0];
93952               var H = null;
93953               var h = null;
93954               var S = null;
93955               var s = null;
93956               var e = null;
93957               var f = null;
93958               S = this._hi + y$1;
93959               e = S - this._hi;
93960               s = S - e;
93961               s = y$1 - e + (this._hi - s);
93962               f = s + this._lo;
93963               H = S + f;
93964               h = f + (S - H);
93965               this._hi = H + h;
93966               this._lo = h + (H - this._hi);
93967               return this
93968             }
93969           } else if (arguments.length === 2) {
93970             var yhi = arguments[0];
93971             var ylo = arguments[1];
93972             var H$1 = null;
93973             var h$1 = null;
93974             var T = null;
93975             var t = null;
93976             var S$1 = null;
93977             var s$1 = null;
93978             var e$1 = null;
93979             var f$1 = null;
93980             S$1 = this._hi + yhi;
93981             T = this._lo + ylo;
93982             e$1 = S$1 - this._hi;
93983             f$1 = T - this._lo;
93984             s$1 = S$1 - e$1;
93985             t = T - f$1;
93986             s$1 = yhi - e$1 + (this._hi - s$1);
93987             t = ylo - f$1 + (this._lo - t);
93988             e$1 = s$1 + T;
93989             H$1 = S$1 + e$1;
93990             h$1 = e$1 + (S$1 - H$1);
93991             e$1 = t + h$1;
93992             var zhi = H$1 + e$1;
93993             var zlo = e$1 + (H$1 - zhi);
93994             this._hi = zhi;
93995             this._lo = zlo;
93996             return this
93997           }
93998         };
93999         DD.prototype.selfMultiply = function selfMultiply () {
94000           if (arguments.length === 1) {
94001             if (arguments[0] instanceof DD) {
94002               var y = arguments[0];
94003               return this.selfMultiply(y._hi, y._lo)
94004             } else if (typeof arguments[0] === 'number') {
94005               var y$1 = arguments[0];
94006               return this.selfMultiply(y$1, 0.0)
94007             }
94008           } else if (arguments.length === 2) {
94009             var yhi = arguments[0];
94010             var ylo = arguments[1];
94011             var hx = null;
94012             var tx = null;
94013             var hy = null;
94014             var ty = null;
94015             var C = null;
94016             var c = null;
94017             C = DD.SPLIT * this._hi;
94018             hx = C - this._hi;
94019             c = DD.SPLIT * yhi;
94020             hx = C - hx;
94021             tx = this._hi - hx;
94022             hy = c - yhi;
94023             C = this._hi * yhi;
94024             hy = c - hy;
94025             ty = yhi - hy;
94026             c = hx * hy - C + hx * ty + tx * hy + tx * ty + (this._hi * ylo + this._lo * yhi);
94027             var zhi = C + c;
94028             hx = C - zhi;
94029             var zlo = c + hx;
94030             this._hi = zhi;
94031             this._lo = zlo;
94032             return this
94033           }
94034         };
94035         DD.prototype.selfSqr = function selfSqr () {
94036           return this.selfMultiply(this)
94037         };
94038         DD.prototype.floor = function floor () {
94039           if (this.isNaN()) { return DD.NaN }
94040           var fhi = Math.floor(this._hi);
94041           var flo = 0.0;
94042           if (fhi === this._hi) {
94043             flo = Math.floor(this._lo);
94044           }
94045           return new DD(fhi, flo)
94046         };
94047         DD.prototype.negate = function negate () {
94048           if (this.isNaN()) { return this }
94049           return new DD(-this._hi, -this._lo)
94050         };
94051         DD.prototype.clone = function clone () {
94052           // try {
94053           // return null
94054           // } catch (ex) {
94055           // if (ex instanceof CloneNotSupportedException) {
94056           //   return null
94057           // } else throw ex
94058           // } finally {}
94059         };
94060         DD.prototype.multiply = function multiply () {
94061           if (arguments[0] instanceof DD) {
94062             var y = arguments[0];
94063             if (y.isNaN()) { return DD.createNaN() }
94064             return DD.copy(this).selfMultiply(y)
94065           } else if (typeof arguments[0] === 'number') {
94066             var y$1 = arguments[0];
94067             if (Double.isNaN(y$1)) { return DD.createNaN() }
94068             return DD.copy(this).selfMultiply(y$1, 0.0)
94069           }
94070         };
94071         DD.prototype.isNaN = function isNaN () {
94072           return Double.isNaN(this._hi)
94073         };
94074         DD.prototype.intValue = function intValue () {
94075           return Math.trunc(this._hi)
94076         };
94077         DD.prototype.toString = function toString () {
94078           var mag = DD.magnitude(this._hi);
94079           if (mag >= -3 && mag <= 20) { return this.toStandardNotation() }
94080           return this.toSciNotation()
94081         };
94082         DD.prototype.toStandardNotation = function toStandardNotation () {
94083           var specialStr = this.getSpecialNumberString();
94084           if (specialStr !== null) { return specialStr }
94085           var magnitude = new Array(1).fill(null);
94086           var sigDigits = this.extractSignificantDigits(true, magnitude);
94087           var decimalPointPos = magnitude[0] + 1;
94088           var num = sigDigits;
94089           if (sigDigits.charAt(0) === '.') {
94090             num = '0' + sigDigits;
94091           } else if (decimalPointPos < 0) {
94092             num = '0.' + DD.stringOfChar('0', -decimalPointPos) + sigDigits;
94093           } else if (sigDigits.indexOf('.') === -1) {
94094             var numZeroes = decimalPointPos - sigDigits.length;
94095             var zeroes = DD.stringOfChar('0', numZeroes);
94096             num = sigDigits + zeroes + '.0';
94097           }
94098           if (this.isNegative()) { return '-' + num }
94099           return num
94100         };
94101         DD.prototype.reciprocal = function reciprocal () {
94102           var hc = null;
94103           var tc = null;
94104           var hy = null;
94105           var ty = null;
94106           var C = null;
94107           var c = null;
94108           var U = null;
94109           var u = null;
94110           C = 1.0 / this._hi;
94111           c = DD.SPLIT * C;
94112           hc = c - C;
94113           u = DD.SPLIT * this._hi;
94114           hc = c - hc;
94115           tc = C - hc;
94116           hy = u - this._hi;
94117           U = C * this._hi;
94118           hy = u - hy;
94119           ty = this._hi - hy;
94120           u = hc * hy - U + hc * ty + tc * hy + tc * ty;
94121           c = (1.0 - U - u - C * this._lo) / this._hi;
94122           var zhi = C + c;
94123           var zlo = C - zhi + c;
94124           return new DD(zhi, zlo)
94125         };
94126         DD.prototype.toSciNotation = function toSciNotation () {
94127           if (this.isZero()) { return DD.SCI_NOT_ZERO }
94128           var specialStr = this.getSpecialNumberString();
94129           if (specialStr !== null) { return specialStr }
94130           var magnitude = new Array(1).fill(null);
94131           var digits = this.extractSignificantDigits(false, magnitude);
94132           var expStr = DD.SCI_NOT_EXPONENT_CHAR + magnitude[0];
94133           if (digits.charAt(0) === '0') {
94134             throw new Error('Found leading zero: ' + digits)
94135           }
94136           var trailingDigits = '';
94137           if (digits.length > 1) { trailingDigits = digits.substring(1); }
94138           var digitsWithDecimal = digits.charAt(0) + '.' + trailingDigits;
94139           if (this.isNegative()) { return '-' + digitsWithDecimal + expStr }
94140           return digitsWithDecimal + expStr
94141         };
94142         DD.prototype.abs = function abs () {
94143           if (this.isNaN()) { return DD.NaN }
94144           if (this.isNegative()) { return this.negate() }
94145           return new DD(this)
94146         };
94147         DD.prototype.isPositive = function isPositive () {
94148           return (this._hi > 0.0 || this._hi === 0.0) && this._lo > 0.0
94149         };
94150         DD.prototype.lt = function lt (y) {
94151           return (this._hi < y._hi || this._hi === y._hi) && this._lo < y._lo
94152         };
94153         DD.prototype.add = function add () {
94154           if (arguments[0] instanceof DD) {
94155             var y = arguments[0];
94156             return DD.copy(this).selfAdd(y)
94157           } else if (typeof arguments[0] === 'number') {
94158             var y$1 = arguments[0];
94159             return DD.copy(this).selfAdd(y$1)
94160           }
94161         };
94162         DD.prototype.init = function init () {
94163           if (arguments.length === 1) {
94164             if (typeof arguments[0] === 'number') {
94165               var x = arguments[0];
94166               this._hi = x;
94167               this._lo = 0.0;
94168             } else if (arguments[0] instanceof DD) {
94169               var dd = arguments[0];
94170               this._hi = dd._hi;
94171               this._lo = dd._lo;
94172             }
94173           } else if (arguments.length === 2) {
94174             var hi = arguments[0];
94175             var lo = arguments[1];
94176             this._hi = hi;
94177             this._lo = lo;
94178           }
94179         };
94180         DD.prototype.gt = function gt (y) {
94181           return (this._hi > y._hi || this._hi === y._hi) && this._lo > y._lo
94182         };
94183         DD.prototype.isNegative = function isNegative () {
94184           return (this._hi < 0.0 || this._hi === 0.0) && this._lo < 0.0
94185         };
94186         DD.prototype.trunc = function trunc () {
94187           if (this.isNaN()) { return DD.NaN }
94188           if (this.isPositive()) { return this.floor(); } else { return this.ceil() }
94189         };
94190         DD.prototype.signum = function signum () {
94191           if (this._hi > 0) { return 1 }
94192           if (this._hi < 0) { return -1 }
94193           if (this._lo > 0) { return 1 }
94194           if (this._lo < 0) { return -1 }
94195           return 0
94196         };
94197         DD.prototype.interfaces_ = function interfaces_ () {
94198           return [Serializable, Comparable, Clonable]
94199         };
94200         DD.prototype.getClass = function getClass () {
94201           return DD
94202         };
94203         DD.sqr = function sqr (x) {
94204           return DD.valueOf(x).selfMultiply(x)
94205         };
94206         DD.valueOf = function valueOf () {
94207           if (typeof arguments[0] === 'string') {
94208             var str = arguments[0];
94209             return DD.parse(str)
94210           } else if (typeof arguments[0] === 'number') {
94211             var x = arguments[0];
94212             return new DD(x)
94213           }
94214         };
94215         DD.sqrt = function sqrt (x) {
94216           return DD.valueOf(x).sqrt()
94217         };
94218         DD.parse = function parse (str) {
94219           var i = 0;
94220           var strlen = str.length;
94221           while (Character.isWhitespace(str.charAt(i))) { i++; }
94222           var isNegative = false;
94223           if (i < strlen) {
94224             var signCh = str.charAt(i);
94225             if (signCh === '-' || signCh === '+') {
94226               i++;
94227               if (signCh === '-') { isNegative = true; }
94228             }
94229           }
94230           var val = new DD();
94231           var numDigits = 0;
94232           var numBeforeDec = 0;
94233           var exp = 0;
94234           while (true) {
94235             if (i >= strlen) { break }
94236             var ch = str.charAt(i);
94237             i++;
94238             if (Character.isDigit(ch)) {
94239               var d = ch - '0';
94240               val.selfMultiply(DD.TEN);
94241               val.selfAdd(d);
94242               numDigits++;
94243               continue
94244             }
94245             if (ch === '.') {
94246               numBeforeDec = numDigits;
94247               continue
94248             }
94249             if (ch === 'e' || ch === 'E') {
94250               var expStr = str.substring(i);
94251               try {
94252                 exp = Integer.parseInt(expStr);
94253               } catch (ex) {
94254                 if (ex instanceof Error) {
94255                   throw new Error('Invalid exponent ' + expStr + ' in string ' + str)
94256                 } else { throw ex }
94257               } finally {}
94258               break
94259             }
94260             throw new Error("Unexpected character '" + ch + "' at position " + i + ' in string ' + str)
94261           }
94262           var val2 = val;
94263           var numDecPlaces = numDigits - numBeforeDec - exp;
94264           if (numDecPlaces === 0) {
94265             val2 = val;
94266           } else if (numDecPlaces > 0) {
94267             var scale = DD.TEN.pow(numDecPlaces);
94268             val2 = val.divide(scale);
94269           } else if (numDecPlaces < 0) {
94270             var scale$1 = DD.TEN.pow(-numDecPlaces);
94271             val2 = val.multiply(scale$1);
94272           }
94273           if (isNegative) {
94274             return val2.negate()
94275           }
94276           return val2
94277         };
94278         DD.createNaN = function createNaN () {
94279           return new DD(Double.NaN, Double.NaN)
94280         };
94281         DD.copy = function copy (dd) {
94282           return new DD(dd)
94283         };
94284         DD.magnitude = function magnitude (x) {
94285           var xAbs = Math.abs(x);
94286           var xLog10 = Math.log(xAbs) / Math.log(10);
94287           var xMag = Math.trunc(Math.floor(xLog10));
94288           var xApprox = Math.pow(10, xMag);
94289           if (xApprox * 10 <= xAbs) { xMag += 1; }
94290           return xMag
94291         };
94292         DD.stringOfChar = function stringOfChar (ch, len) {
94293           var buf = new StringBuffer();
94294           for (var i = 0; i < len; i++) {
94295             buf.append(ch);
94296           }
94297           return buf.toString()
94298         };
94299         staticAccessors$7.PI.get = function () { return new DD(3.141592653589793116e+00, 1.224646799147353207e-16) };
94300         staticAccessors$7.TWO_PI.get = function () { return new DD(6.283185307179586232e+00, 2.449293598294706414e-16) };
94301         staticAccessors$7.PI_2.get = function () { return new DD(1.570796326794896558e+00, 6.123233995736766036e-17) };
94302         staticAccessors$7.E.get = function () { return new DD(2.718281828459045091e+00, 1.445646891729250158e-16) };
94303         staticAccessors$7.NaN.get = function () { return new DD(Double.NaN, Double.NaN) };
94304         staticAccessors$7.EPS.get = function () { return 1.23259516440783e-32 };
94305         staticAccessors$7.SPLIT.get = function () { return 134217729.0 };
94306         staticAccessors$7.MAX_PRINT_DIGITS.get = function () { return 32 };
94307         staticAccessors$7.TEN.get = function () { return DD.valueOf(10.0) };
94308         staticAccessors$7.ONE.get = function () { return DD.valueOf(1.0) };
94309         staticAccessors$7.SCI_NOT_EXPONENT_CHAR.get = function () { return 'E' };
94310         staticAccessors$7.SCI_NOT_ZERO.get = function () { return '0.0E0' };
94311
94312         Object.defineProperties( DD, staticAccessors$7 );
94313
94314         var CGAlgorithmsDD = function CGAlgorithmsDD () {};
94315
94316         var staticAccessors$6 = { DP_SAFE_EPSILON: { configurable: true } };
94317
94318         CGAlgorithmsDD.prototype.interfaces_ = function interfaces_ () {
94319           return []
94320         };
94321         CGAlgorithmsDD.prototype.getClass = function getClass () {
94322           return CGAlgorithmsDD
94323         };
94324         CGAlgorithmsDD.orientationIndex = function orientationIndex (p1, p2, q) {
94325           var index = CGAlgorithmsDD.orientationIndexFilter(p1, p2, q);
94326           if (index <= 1) { return index }
94327           var dx1 = DD.valueOf(p2.x).selfAdd(-p1.x);
94328           var dy1 = DD.valueOf(p2.y).selfAdd(-p1.y);
94329           var dx2 = DD.valueOf(q.x).selfAdd(-p2.x);
94330           var dy2 = DD.valueOf(q.y).selfAdd(-p2.y);
94331           return dx1.selfMultiply(dy2).selfSubtract(dy1.selfMultiply(dx2)).signum()
94332         };
94333         CGAlgorithmsDD.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
94334           var det = x1.multiply(y2).selfSubtract(y1.multiply(x2));
94335           return det.signum()
94336         };
94337         CGAlgorithmsDD.intersection = function intersection (p1, p2, q1, q2) {
94338           var denom1 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p2.x).selfSubtract(p1.x));
94339           var denom2 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p2.y).selfSubtract(p1.y));
94340           var denom = denom1.subtract(denom2);
94341           var numx1 = DD.valueOf(q2.x).selfSubtract(q1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94342           var numx2 = DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94343           var numx = numx1.subtract(numx2);
94344           var fracP = numx.selfDivide(denom).doubleValue();
94345           var x = DD.valueOf(p1.x).selfAdd(DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(fracP)).doubleValue();
94346           var numy1 = DD.valueOf(p2.x).selfSubtract(p1.x).selfMultiply(DD.valueOf(p1.y).selfSubtract(q1.y));
94347           var numy2 = DD.valueOf(p2.y).selfSubtract(p1.y).selfMultiply(DD.valueOf(p1.x).selfSubtract(q1.x));
94348           var numy = numy1.subtract(numy2);
94349           var fracQ = numy.selfDivide(denom).doubleValue();
94350           var y = DD.valueOf(q1.y).selfAdd(DD.valueOf(q2.y).selfSubtract(q1.y).selfMultiply(fracQ)).doubleValue();
94351           return new Coordinate(x, y)
94352         };
94353         CGAlgorithmsDD.orientationIndexFilter = function orientationIndexFilter (pa, pb, pc) {
94354           var detsum = null;
94355           var detleft = (pa.x - pc.x) * (pb.y - pc.y);
94356           var detright = (pa.y - pc.y) * (pb.x - pc.x);
94357           var det = detleft - detright;
94358           if (detleft > 0.0) {
94359             if (detright <= 0.0) {
94360               return CGAlgorithmsDD.signum(det)
94361             } else {
94362               detsum = detleft + detright;
94363             }
94364           } else if (detleft < 0.0) {
94365             if (detright >= 0.0) {
94366               return CGAlgorithmsDD.signum(det)
94367             } else {
94368               detsum = -detleft - detright;
94369             }
94370           } else {
94371             return CGAlgorithmsDD.signum(det)
94372           }
94373           var errbound = CGAlgorithmsDD.DP_SAFE_EPSILON * detsum;
94374           if (det >= errbound || -det >= errbound) {
94375             return CGAlgorithmsDD.signum(det)
94376           }
94377           return 2
94378         };
94379         CGAlgorithmsDD.signum = function signum (x) {
94380           if (x > 0) { return 1 }
94381           if (x < 0) { return -1 }
94382           return 0
94383         };
94384         staticAccessors$6.DP_SAFE_EPSILON.get = function () { return 1e-15 };
94385
94386         Object.defineProperties( CGAlgorithmsDD, staticAccessors$6 );
94387
94388         var CoordinateSequence = function CoordinateSequence () {};
94389
94390         var staticAccessors$8 = { X: { configurable: true },Y: { configurable: true },Z: { configurable: true },M: { configurable: true } };
94391
94392         staticAccessors$8.X.get = function () { return 0 };
94393         staticAccessors$8.Y.get = function () { return 1 };
94394         staticAccessors$8.Z.get = function () { return 2 };
94395         staticAccessors$8.M.get = function () { return 3 };
94396         CoordinateSequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {};
94397         CoordinateSequence.prototype.size = function size () {};
94398         CoordinateSequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {};
94399         CoordinateSequence.prototype.getCoordinate = function getCoordinate () {};
94400         CoordinateSequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {};
94401         CoordinateSequence.prototype.getDimension = function getDimension () {};
94402         CoordinateSequence.prototype.getX = function getX (index) {};
94403         CoordinateSequence.prototype.clone = function clone () {};
94404         CoordinateSequence.prototype.expandEnvelope = function expandEnvelope (env) {};
94405         CoordinateSequence.prototype.copy = function copy () {};
94406         CoordinateSequence.prototype.getY = function getY (index) {};
94407         CoordinateSequence.prototype.toCoordinateArray = function toCoordinateArray () {};
94408         CoordinateSequence.prototype.interfaces_ = function interfaces_ () {
94409           return [Clonable]
94410         };
94411         CoordinateSequence.prototype.getClass = function getClass () {
94412           return CoordinateSequence
94413         };
94414
94415         Object.defineProperties( CoordinateSequence, staticAccessors$8 );
94416
94417         var Exception = function Exception () {};
94418
94419         var NotRepresentableException = (function (Exception$$1) {
94420           function NotRepresentableException () {
94421             Exception$$1.call(this, 'Projective point not representable on the Cartesian plane.');
94422           }
94423
94424           if ( Exception$$1 ) { NotRepresentableException.__proto__ = Exception$$1; }
94425           NotRepresentableException.prototype = Object.create( Exception$$1 && Exception$$1.prototype );
94426           NotRepresentableException.prototype.constructor = NotRepresentableException;
94427           NotRepresentableException.prototype.interfaces_ = function interfaces_ () {
94428             return []
94429           };
94430           NotRepresentableException.prototype.getClass = function getClass () {
94431             return NotRepresentableException
94432           };
94433
94434           return NotRepresentableException;
94435         }(Exception));
94436
94437         var System = function System () {};
94438
94439         System.arraycopy = function arraycopy (src, srcPos, dest, destPos, len) {
94440           var c = 0;
94441           for (var i = srcPos; i < srcPos + len; i++) {
94442             dest[destPos + c] = src[i];
94443             c++;
94444           }
94445         };
94446
94447         System.getProperty = function getProperty (name) {
94448           return {
94449             'line.separator': '\n'
94450           }[name]
94451         };
94452
94453         var HCoordinate = function HCoordinate () {
94454           this.x = null;
94455           this.y = null;
94456           this.w = null;
94457           if (arguments.length === 0) {
94458             this.x = 0.0;
94459             this.y = 0.0;
94460             this.w = 1.0;
94461           } else if (arguments.length === 1) {
94462             var p = arguments[0];
94463             this.x = p.x;
94464             this.y = p.y;
94465             this.w = 1.0;
94466           } else if (arguments.length === 2) {
94467             if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
94468               var _x = arguments[0];
94469               var _y = arguments[1];
94470               this.x = _x;
94471               this.y = _y;
94472               this.w = 1.0;
94473             } else if (arguments[0] instanceof HCoordinate && arguments[1] instanceof HCoordinate) {
94474               var p1 = arguments[0];
94475               var p2 = arguments[1];
94476               this.x = p1.y * p2.w - p2.y * p1.w;
94477               this.y = p2.x * p1.w - p1.x * p2.w;
94478               this.w = p1.x * p2.y - p2.x * p1.y;
94479             } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
94480               var p1$1 = arguments[0];
94481               var p2$1 = arguments[1];
94482               this.x = p1$1.y - p2$1.y;
94483               this.y = p2$1.x - p1$1.x;
94484               this.w = p1$1.x * p2$1.y - p2$1.x * p1$1.y;
94485             }
94486           } else if (arguments.length === 3) {
94487             var _x$1 = arguments[0];
94488             var _y$1 = arguments[1];
94489             var _w = arguments[2];
94490             this.x = _x$1;
94491             this.y = _y$1;
94492             this.w = _w;
94493           } else if (arguments.length === 4) {
94494             var p1$2 = arguments[0];
94495             var p2$2 = arguments[1];
94496             var q1 = arguments[2];
94497             var q2 = arguments[3];
94498             var px = p1$2.y - p2$2.y;
94499             var py = p2$2.x - p1$2.x;
94500             var pw = p1$2.x * p2$2.y - p2$2.x * p1$2.y;
94501             var qx = q1.y - q2.y;
94502             var qy = q2.x - q1.x;
94503             var qw = q1.x * q2.y - q2.x * q1.y;
94504             this.x = py * qw - qy * pw;
94505             this.y = qx * pw - px * qw;
94506             this.w = px * qy - qx * py;
94507           }
94508         };
94509         HCoordinate.prototype.getY = function getY () {
94510           var a = this.y / this.w;
94511           if (Double.isNaN(a) || Double.isInfinite(a)) {
94512             throw new NotRepresentableException()
94513           }
94514           return a
94515         };
94516         HCoordinate.prototype.getX = function getX () {
94517           var a = this.x / this.w;
94518           if (Double.isNaN(a) || Double.isInfinite(a)) {
94519             throw new NotRepresentableException()
94520           }
94521           return a
94522         };
94523         HCoordinate.prototype.getCoordinate = function getCoordinate () {
94524           var p = new Coordinate();
94525           p.x = this.getX();
94526           p.y = this.getY();
94527           return p
94528         };
94529         HCoordinate.prototype.interfaces_ = function interfaces_ () {
94530           return []
94531         };
94532         HCoordinate.prototype.getClass = function getClass () {
94533           return HCoordinate
94534         };
94535         HCoordinate.intersection = function intersection (p1, p2, q1, q2) {
94536           var px = p1.y - p2.y;
94537           var py = p2.x - p1.x;
94538           var pw = p1.x * p2.y - p2.x * p1.y;
94539           var qx = q1.y - q2.y;
94540           var qy = q2.x - q1.x;
94541           var qw = q1.x * q2.y - q2.x * q1.y;
94542           var x = py * qw - qy * pw;
94543           var y = qx * pw - px * qw;
94544           var w = px * qy - qx * py;
94545           var xInt = x / w;
94546           var yInt = y / w;
94547           if (Double.isNaN(xInt) || (Double.isInfinite(xInt) || Double.isNaN(yInt)) || Double.isInfinite(yInt)) {
94548             throw new NotRepresentableException()
94549           }
94550           return new Coordinate(xInt, yInt)
94551         };
94552
94553         var Envelope = function Envelope () {
94554           this._minx = null;
94555           this._maxx = null;
94556           this._miny = null;
94557           this._maxy = null;
94558           if (arguments.length === 0) {
94559             this.init();
94560           } else if (arguments.length === 1) {
94561             if (arguments[0] instanceof Coordinate) {
94562               var p = arguments[0];
94563               this.init(p.x, p.x, p.y, p.y);
94564             } else if (arguments[0] instanceof Envelope) {
94565               var env = arguments[0];
94566               this.init(env);
94567             }
94568           } else if (arguments.length === 2) {
94569             var p1 = arguments[0];
94570             var p2 = arguments[1];
94571             this.init(p1.x, p2.x, p1.y, p2.y);
94572           } else if (arguments.length === 4) {
94573             var x1 = arguments[0];
94574             var x2 = arguments[1];
94575             var y1 = arguments[2];
94576             var y2 = arguments[3];
94577             this.init(x1, x2, y1, y2);
94578           }
94579         };
94580
94581         var staticAccessors$9 = { serialVersionUID: { configurable: true } };
94582         Envelope.prototype.getArea = function getArea () {
94583           return this.getWidth() * this.getHeight()
94584         };
94585         Envelope.prototype.equals = function equals (other) {
94586           if (!(other instanceof Envelope)) {
94587             return false
94588           }
94589           var otherEnvelope = other;
94590           if (this.isNull()) {
94591             return otherEnvelope.isNull()
94592           }
94593           return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY()
94594         };
94595         Envelope.prototype.intersection = function intersection (env) {
94596           if (this.isNull() || env.isNull() || !this.intersects(env)) { return new Envelope() }
94597           var intMinX = this._minx > env._minx ? this._minx : env._minx;
94598           var intMinY = this._miny > env._miny ? this._miny : env._miny;
94599           var intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx;
94600           var intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy;
94601           return new Envelope(intMinX, intMaxX, intMinY, intMaxY)
94602         };
94603         Envelope.prototype.isNull = function isNull () {
94604           return this._maxx < this._minx
94605         };
94606         Envelope.prototype.getMaxX = function getMaxX () {
94607           return this._maxx
94608         };
94609         Envelope.prototype.covers = function covers () {
94610           if (arguments.length === 1) {
94611             if (arguments[0] instanceof Coordinate) {
94612               var p = arguments[0];
94613               return this.covers(p.x, p.y)
94614             } else if (arguments[0] instanceof Envelope) {
94615               var other = arguments[0];
94616               if (this.isNull() || other.isNull()) {
94617                 return false
94618               }
94619               return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy
94620             }
94621           } else if (arguments.length === 2) {
94622             var x = arguments[0];
94623             var y = arguments[1];
94624             if (this.isNull()) { return false }
94625             return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy
94626           }
94627         };
94628         Envelope.prototype.intersects = function intersects () {
94629           if (arguments.length === 1) {
94630             if (arguments[0] instanceof Envelope) {
94631               var other = arguments[0];
94632               if (this.isNull() || other.isNull()) {
94633                 return false
94634               }
94635               return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny)
94636             } else if (arguments[0] instanceof Coordinate) {
94637               var p = arguments[0];
94638               return this.intersects(p.x, p.y)
94639             }
94640           } else if (arguments.length === 2) {
94641             var x = arguments[0];
94642             var y = arguments[1];
94643             if (this.isNull()) { return false }
94644             return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny)
94645           }
94646         };
94647         Envelope.prototype.getMinY = function getMinY () {
94648           return this._miny
94649         };
94650         Envelope.prototype.getMinX = function getMinX () {
94651           return this._minx
94652         };
94653         Envelope.prototype.expandToInclude = function expandToInclude () {
94654           if (arguments.length === 1) {
94655             if (arguments[0] instanceof Coordinate) {
94656               var p = arguments[0];
94657               this.expandToInclude(p.x, p.y);
94658             } else if (arguments[0] instanceof Envelope) {
94659               var other = arguments[0];
94660               if (other.isNull()) {
94661                 return null
94662               }
94663               if (this.isNull()) {
94664                 this._minx = other.getMinX();
94665                 this._maxx = other.getMaxX();
94666                 this._miny = other.getMinY();
94667                 this._maxy = other.getMaxY();
94668               } else {
94669                 if (other._minx < this._minx) {
94670                   this._minx = other._minx;
94671                 }
94672                 if (other._maxx > this._maxx) {
94673                   this._maxx = other._maxx;
94674                 }
94675                 if (other._miny < this._miny) {
94676                   this._miny = other._miny;
94677                 }
94678                 if (other._maxy > this._maxy) {
94679                   this._maxy = other._maxy;
94680                 }
94681               }
94682             }
94683           } else if (arguments.length === 2) {
94684             var x = arguments[0];
94685             var y = arguments[1];
94686             if (this.isNull()) {
94687               this._minx = x;
94688               this._maxx = x;
94689               this._miny = y;
94690               this._maxy = y;
94691             } else {
94692               if (x < this._minx) {
94693                 this._minx = x;
94694               }
94695               if (x > this._maxx) {
94696                 this._maxx = x;
94697               }
94698               if (y < this._miny) {
94699                 this._miny = y;
94700               }
94701               if (y > this._maxy) {
94702                 this._maxy = y;
94703               }
94704             }
94705           }
94706         };
94707         Envelope.prototype.minExtent = function minExtent () {
94708           if (this.isNull()) { return 0.0 }
94709           var w = this.getWidth();
94710           var h = this.getHeight();
94711           if (w < h) { return w }
94712           return h
94713         };
94714         Envelope.prototype.getWidth = function getWidth () {
94715           if (this.isNull()) {
94716             return 0
94717           }
94718           return this._maxx - this._minx
94719         };
94720         Envelope.prototype.compareTo = function compareTo (o) {
94721           var env = o;
94722           if (this.isNull()) {
94723             if (env.isNull()) { return 0 }
94724             return -1
94725           } else {
94726             if (env.isNull()) { return 1 }
94727           }
94728           if (this._minx < env._minx) { return -1 }
94729           if (this._minx > env._minx) { return 1 }
94730           if (this._miny < env._miny) { return -1 }
94731           if (this._miny > env._miny) { return 1 }
94732           if (this._maxx < env._maxx) { return -1 }
94733           if (this._maxx > env._maxx) { return 1 }
94734           if (this._maxy < env._maxy) { return -1 }
94735           if (this._maxy > env._maxy) { return 1 }
94736           return 0
94737         };
94738         Envelope.prototype.translate = function translate (transX, transY) {
94739           if (this.isNull()) {
94740             return null
94741           }
94742           this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY);
94743         };
94744         Envelope.prototype.toString = function toString () {
94745           return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']'
94746         };
94747         Envelope.prototype.setToNull = function setToNull () {
94748           this._minx = 0;
94749           this._maxx = -1;
94750           this._miny = 0;
94751           this._maxy = -1;
94752         };
94753         Envelope.prototype.getHeight = function getHeight () {
94754           if (this.isNull()) {
94755             return 0
94756           }
94757           return this._maxy - this._miny
94758         };
94759         Envelope.prototype.maxExtent = function maxExtent () {
94760           if (this.isNull()) { return 0.0 }
94761           var w = this.getWidth();
94762           var h = this.getHeight();
94763           if (w > h) { return w }
94764           return h
94765         };
94766         Envelope.prototype.expandBy = function expandBy () {
94767           if (arguments.length === 1) {
94768             var distance = arguments[0];
94769             this.expandBy(distance, distance);
94770           } else if (arguments.length === 2) {
94771             var deltaX = arguments[0];
94772             var deltaY = arguments[1];
94773             if (this.isNull()) { return null }
94774             this._minx -= deltaX;
94775             this._maxx += deltaX;
94776             this._miny -= deltaY;
94777             this._maxy += deltaY;
94778             if (this._minx > this._maxx || this._miny > this._maxy) { this.setToNull(); }
94779           }
94780         };
94781         Envelope.prototype.contains = function contains () {
94782           if (arguments.length === 1) {
94783             if (arguments[0] instanceof Envelope) {
94784               var other = arguments[0];
94785               return this.covers(other)
94786             } else if (arguments[0] instanceof Coordinate) {
94787               var p = arguments[0];
94788               return this.covers(p)
94789             }
94790           } else if (arguments.length === 2) {
94791             var x = arguments[0];
94792             var y = arguments[1];
94793             return this.covers(x, y)
94794           }
94795         };
94796         Envelope.prototype.centre = function centre () {
94797           if (this.isNull()) { return null }
94798           return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0)
94799         };
94800         Envelope.prototype.init = function init () {
94801           if (arguments.length === 0) {
94802             this.setToNull();
94803           } else if (arguments.length === 1) {
94804             if (arguments[0] instanceof Coordinate) {
94805               var p = arguments[0];
94806               this.init(p.x, p.x, p.y, p.y);
94807             } else if (arguments[0] instanceof Envelope) {
94808               var env = arguments[0];
94809               this._minx = env._minx;
94810               this._maxx = env._maxx;
94811               this._miny = env._miny;
94812               this._maxy = env._maxy;
94813             }
94814           } else if (arguments.length === 2) {
94815             var p1 = arguments[0];
94816             var p2 = arguments[1];
94817             this.init(p1.x, p2.x, p1.y, p2.y);
94818           } else if (arguments.length === 4) {
94819             var x1 = arguments[0];
94820             var x2 = arguments[1];
94821             var y1 = arguments[2];
94822             var y2 = arguments[3];
94823             if (x1 < x2) {
94824               this._minx = x1;
94825               this._maxx = x2;
94826             } else {
94827               this._minx = x2;
94828               this._maxx = x1;
94829             }
94830             if (y1 < y2) {
94831               this._miny = y1;
94832               this._maxy = y2;
94833             } else {
94834               this._miny = y2;
94835               this._maxy = y1;
94836             }
94837           }
94838         };
94839         Envelope.prototype.getMaxY = function getMaxY () {
94840           return this._maxy
94841         };
94842         Envelope.prototype.distance = function distance (env) {
94843           if (this.intersects(env)) { return 0 }
94844           var dx = 0.0;
94845           if (this._maxx < env._minx) { dx = env._minx - this._maxx; } else if (this._minx > env._maxx) { dx = this._minx - env._maxx; }
94846           var dy = 0.0;
94847           if (this._maxy < env._miny) { dy = env._miny - this._maxy; } else if (this._miny > env._maxy) { dy = this._miny - env._maxy; }
94848           if (dx === 0.0) { return dy }
94849           if (dy === 0.0) { return dx }
94850           return Math.sqrt(dx * dx + dy * dy)
94851         };
94852         Envelope.prototype.hashCode = function hashCode () {
94853           var result = 17;
94854           result = 37 * result + Coordinate.hashCode(this._minx);
94855           result = 37 * result + Coordinate.hashCode(this._maxx);
94856           result = 37 * result + Coordinate.hashCode(this._miny);
94857           result = 37 * result + Coordinate.hashCode(this._maxy);
94858           return result
94859         };
94860         Envelope.prototype.interfaces_ = function interfaces_ () {
94861           return [Comparable, Serializable]
94862         };
94863         Envelope.prototype.getClass = function getClass () {
94864           return Envelope
94865         };
94866         Envelope.intersects = function intersects () {
94867           if (arguments.length === 3) {
94868             var p1 = arguments[0];
94869             var p2 = arguments[1];
94870             var q = arguments[2];
94871             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))) {
94872               return true
94873             }
94874             return false
94875           } else if (arguments.length === 4) {
94876             var p1$1 = arguments[0];
94877             var p2$1 = arguments[1];
94878             var q1 = arguments[2];
94879             var q2 = arguments[3];
94880             var minq = Math.min(q1.x, q2.x);
94881             var maxq = Math.max(q1.x, q2.x);
94882             var minp = Math.min(p1$1.x, p2$1.x);
94883             var maxp = Math.max(p1$1.x, p2$1.x);
94884             if (minp > maxq) { return false }
94885             if (maxp < minq) { return false }
94886             minq = Math.min(q1.y, q2.y);
94887             maxq = Math.max(q1.y, q2.y);
94888             minp = Math.min(p1$1.y, p2$1.y);
94889             maxp = Math.max(p1$1.y, p2$1.y);
94890             if (minp > maxq) { return false }
94891             if (maxp < minq) { return false }
94892             return true
94893           }
94894         };
94895         staticAccessors$9.serialVersionUID.get = function () { return 5873921885273102420 };
94896
94897         Object.defineProperties( Envelope, staticAccessors$9 );
94898
94899         var regExes = {
94900           'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
94901           'emptyTypeStr': /^\s*(\w+)\s*EMPTY\s*$/,
94902           'spaces': /\s+/,
94903           'parenComma': /\)\s*,\s*\(/,
94904           'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/, // can't use {2} here
94905           'trimParens': /^\s*\(?(.*?)\)?\s*$/
94906         };
94907
94908         /**
94909          * Class for reading and writing Well-Known Text.
94910          *
94911          * NOTE: Adapted from OpenLayers 2.11 implementation.
94912          */
94913
94914         /** Create a new parser for WKT
94915          *
94916          * @param {GeometryFactory} geometryFactory
94917          * @return An instance of WKTParser.
94918          * @constructor
94919          * @private
94920          */
94921         var WKTParser = function WKTParser (geometryFactory) {
94922           this.geometryFactory = geometryFactory || new GeometryFactory();
94923         };
94924         /**
94925          * Deserialize a WKT string and return a geometry. Supports WKT for POINT,
94926          * MULTIPOINT, LINESTRING, LINEARRING, MULTILINESTRING, POLYGON, MULTIPOLYGON,
94927          * and GEOMETRYCOLLECTION.
94928          *
94929          * @param {String} wkt A WKT string.
94930          * @return {Geometry} A geometry instance.
94931          * @private
94932          */
94933         WKTParser.prototype.read = function read (wkt) {
94934           var geometry, type, str;
94935           wkt = wkt.replace(/[\n\r]/g, ' ');
94936           var matches = regExes.typeStr.exec(wkt);
94937           if (wkt.search('EMPTY') !== -1) {
94938             matches = regExes.emptyTypeStr.exec(wkt);
94939             matches[2] = undefined;
94940           }
94941           if (matches) {
94942             type = matches[1].toLowerCase();
94943             str = matches[2];
94944             if (parse$1[type]) {
94945               geometry = parse$1[type].apply(this, [str]);
94946             }
94947           }
94948
94949           if (geometry === undefined) { throw new Error('Could not parse WKT ' + wkt) }
94950
94951           return geometry
94952         };
94953
94954         /**
94955          * Serialize a geometry into a WKT string.
94956          *
94957          * @param {Geometry} geometry A feature or array of features.
94958          * @return {String} The WKT string representation of the input geometries.
94959          * @private
94960          */
94961         WKTParser.prototype.write = function write (geometry) {
94962           return this.extractGeometry(geometry)
94963         };
94964
94965         /**
94966          * Entry point to construct the WKT for a single Geometry object.
94967          *
94968          * @param {Geometry} geometry
94969          * @return {String} A WKT string of representing the geometry.
94970          * @private
94971          */
94972         WKTParser.prototype.extractGeometry = function extractGeometry (geometry) {
94973           var type = geometry.getGeometryType().toLowerCase();
94974           if (!extract$1[type]) {
94975             return null
94976           }
94977           var wktType = type.toUpperCase();
94978           var data;
94979           if (geometry.isEmpty()) {
94980             data = wktType + ' EMPTY';
94981           } else {
94982             data = wktType + '(' + extract$1[type].apply(this, [geometry]) + ')';
94983           }
94984           return data
94985         };
94986
94987         /**
94988          * Object with properties corresponding to the geometry types. Property values
94989          * are functions that do the actual data extraction.
94990          * @private
94991          */
94992         var extract$1 = {
94993           coordinate: function coordinate (coordinate$1) {
94994             return coordinate$1.x + ' ' + coordinate$1.y
94995           },
94996
94997           /**
94998            * Return a space delimited string of point coordinates.
94999            *
95000            * @param {Point}
95001            *          point
95002            * @return {String} A string of coordinates representing the point.
95003            */
95004           point: function point (point$1) {
95005             return extract$1.coordinate.call(this, point$1._coordinates._coordinates[0])
95006           },
95007
95008           /**
95009            * Return a comma delimited string of point coordinates from a multipoint.
95010            *
95011            * @param {MultiPoint}
95012            *          multipoint
95013            * @return {String} A string of point coordinate strings representing the
95014            *         multipoint.
95015            */
95016           multipoint: function multipoint (multipoint$1) {
95017             var this$1 = this;
95018
95019             var array = [];
95020             for (var i = 0, len = multipoint$1._geometries.length; i < len; ++i) {
95021               array.push('(' + extract$1.point.apply(this$1, [multipoint$1._geometries[i]]) + ')');
95022             }
95023             return array.join(',')
95024           },
95025
95026           /**
95027            * Return a comma delimited string of point coordinates from a line.
95028            *
95029            * @param {LineString} linestring
95030            * @return {String} A string of point coordinate strings representing the linestring.
95031            */
95032           linestring: function linestring (linestring$1) {
95033             var this$1 = this;
95034
95035             var array = [];
95036             for (var i = 0, len = linestring$1._points._coordinates.length; i < len; ++i) {
95037               array.push(extract$1.coordinate.apply(this$1, [linestring$1._points._coordinates[i]]));
95038             }
95039             return array.join(',')
95040           },
95041
95042           linearring: function linearring (linearring$1) {
95043             var this$1 = this;
95044
95045             var array = [];
95046             for (var i = 0, len = linearring$1._points._coordinates.length; i < len; ++i) {
95047               array.push(extract$1.coordinate.apply(this$1, [linearring$1._points._coordinates[i]]));
95048             }
95049             return array.join(',')
95050           },
95051
95052           /**
95053            * Return a comma delimited string of linestring strings from a
95054            * multilinestring.
95055            *
95056            * @param {MultiLineString} multilinestring
95057            * @return {String} A string of of linestring strings representing the multilinestring.
95058            */
95059           multilinestring: function multilinestring (multilinestring$1) {
95060             var this$1 = this;
95061
95062             var array = [];
95063             for (var i = 0, len = multilinestring$1._geometries.length; i < len; ++i) {
95064               array.push('(' +
95065                 extract$1.linestring.apply(this$1, [multilinestring$1._geometries[i]]) +
95066                 ')');
95067             }
95068             return array.join(',')
95069           },
95070
95071           /**
95072            * Return a comma delimited string of linear ring arrays from a polygon.
95073            *
95074            * @param {Polygon} polygon
95075            * @return {String} An array of linear ring arrays representing the polygon.
95076            */
95077           polygon: function polygon (polygon$1) {
95078             var this$1 = this;
95079
95080             var array = [];
95081             array.push('(' + extract$1.linestring.apply(this, [polygon$1._shell]) + ')');
95082             for (var i = 0, len = polygon$1._holes.length; i < len; ++i) {
95083               array.push('(' + extract$1.linestring.apply(this$1, [polygon$1._holes[i]]) + ')');
95084             }
95085             return array.join(',')
95086           },
95087
95088           /**
95089            * Return an array of polygon arrays from a multipolygon.
95090            *
95091            * @param {MultiPolygon} multipolygon
95092            * @return {String} An array of polygon arrays representing the multipolygon.
95093            */
95094           multipolygon: function multipolygon (multipolygon$1) {
95095             var this$1 = this;
95096
95097             var array = [];
95098             for (var i = 0, len = multipolygon$1._geometries.length; i < len; ++i) {
95099               array.push('(' + extract$1.polygon.apply(this$1, [multipolygon$1._geometries[i]]) + ')');
95100             }
95101             return array.join(',')
95102           },
95103
95104           /**
95105            * Return the WKT portion between 'GEOMETRYCOLLECTION(' and ')' for an
95106            * geometrycollection.
95107            *
95108            * @param {GeometryCollection} collection
95109            * @return {String} internal WKT representation of the collection.
95110            */
95111           geometrycollection: function geometrycollection (collection) {
95112             var this$1 = this;
95113
95114             var array = [];
95115             for (var i = 0, len = collection._geometries.length; i < len; ++i) {
95116               array.push(this$1.extractGeometry(collection._geometries[i]));
95117             }
95118             return array.join(',')
95119           }
95120         };
95121
95122         /**
95123          * Object with properties corresponding to the geometry types. Property values
95124          * are functions that do the actual parsing.
95125          * @private
95126          */
95127         var parse$1 = {
95128           /**
95129            * Return point geometry given a point WKT fragment.
95130            *
95131            * @param {String} str A WKT fragment representing the point.
95132            * @return {Point} A point geometry.
95133            * @private
95134            */
95135           point: function point (str) {
95136             if (str === undefined) {
95137               return this.geometryFactory.createPoint()
95138             }
95139
95140             var coords = str.trim().split(regExes.spaces);
95141             return this.geometryFactory.createPoint(new Coordinate(Number.parseFloat(coords[0]),
95142               Number.parseFloat(coords[1])))
95143           },
95144
95145           /**
95146            * Return a multipoint geometry given a multipoint WKT fragment.
95147            *
95148            * @param {String} str A WKT fragment representing the multipoint.
95149            * @return {Point} A multipoint feature.
95150            * @private
95151            */
95152           multipoint: function multipoint (str) {
95153             var this$1 = this;
95154
95155             if (str === undefined) {
95156               return this.geometryFactory.createMultiPoint()
95157             }
95158
95159             var point;
95160             var points = str.trim().split(',');
95161             var components = [];
95162             for (var i = 0, len = points.length; i < len; ++i) {
95163               point = points[i].replace(regExes.trimParens, '$1');
95164               components.push(parse$1.point.apply(this$1, [point]));
95165             }
95166             return this.geometryFactory.createMultiPoint(components)
95167           },
95168
95169           /**
95170            * Return a linestring geometry given a linestring WKT fragment.
95171            *
95172            * @param {String} str A WKT fragment representing the linestring.
95173            * @return {LineString} A linestring geometry.
95174            * @private
95175            */
95176           linestring: function linestring (str) {
95177             if (str === undefined) {
95178               return this.geometryFactory.createLineString()
95179             }
95180
95181             var points = str.trim().split(',');
95182             var components = [];
95183             var coords;
95184             for (var i = 0, len = points.length; i < len; ++i) {
95185               coords = points[i].trim().split(regExes.spaces);
95186               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95187             }
95188             return this.geometryFactory.createLineString(components)
95189           },
95190
95191           /**
95192            * Return a linearring geometry given a linearring WKT fragment.
95193            *
95194            * @param {String} str A WKT fragment representing the linearring.
95195            * @return {LinearRing} A linearring geometry.
95196            * @private
95197            */
95198           linearring: function linearring (str) {
95199             if (str === undefined) {
95200               return this.geometryFactory.createLinearRing()
95201             }
95202
95203             var points = str.trim().split(',');
95204             var components = [];
95205             var coords;
95206             for (var i = 0, len = points.length; i < len; ++i) {
95207               coords = points[i].trim().split(regExes.spaces);
95208               components.push(new Coordinate(Number.parseFloat(coords[0]), Number.parseFloat(coords[1])));
95209             }
95210             return this.geometryFactory.createLinearRing(components)
95211           },
95212
95213           /**
95214            * Return a multilinestring geometry given a multilinestring WKT fragment.
95215            *
95216            * @param {String} str A WKT fragment representing the multilinestring.
95217            * @return {MultiLineString} A multilinestring geometry.
95218            * @private
95219            */
95220           multilinestring: function multilinestring (str) {
95221             var this$1 = this;
95222
95223             if (str === undefined) {
95224               return this.geometryFactory.createMultiLineString()
95225             }
95226
95227             var line;
95228             var lines = str.trim().split(regExes.parenComma);
95229             var components = [];
95230             for (var i = 0, len = lines.length; i < len; ++i) {
95231               line = lines[i].replace(regExes.trimParens, '$1');
95232               components.push(parse$1.linestring.apply(this$1, [line]));
95233             }
95234             return this.geometryFactory.createMultiLineString(components)
95235           },
95236
95237           /**
95238            * Return a polygon geometry given a polygon WKT fragment.
95239            *
95240            * @param {String} str A WKT fragment representing the polygon.
95241            * @return {Polygon} A polygon geometry.
95242            * @private
95243            */
95244           polygon: function polygon (str) {
95245             var this$1 = this;
95246
95247             if (str === undefined) {
95248               return this.geometryFactory.createPolygon()
95249             }
95250
95251             var ring, linestring, linearring;
95252             var rings = str.trim().split(regExes.parenComma);
95253             var shell;
95254             var holes = [];
95255             for (var i = 0, len = rings.length; i < len; ++i) {
95256               ring = rings[i].replace(regExes.trimParens, '$1');
95257               linestring = parse$1.linestring.apply(this$1, [ring]);
95258               linearring = this$1.geometryFactory.createLinearRing(linestring._points);
95259               if (i === 0) {
95260                 shell = linearring;
95261               } else {
95262                 holes.push(linearring);
95263               }
95264             }
95265             return this.geometryFactory.createPolygon(shell, holes)
95266           },
95267
95268           /**
95269            * Return a multipolygon geometry given a multipolygon WKT fragment.
95270            *
95271            * @param {String} str A WKT fragment representing the multipolygon.
95272            * @return {MultiPolygon} A multipolygon geometry.
95273            * @private
95274            */
95275           multipolygon: function multipolygon (str) {
95276             var this$1 = this;
95277
95278             if (str === undefined) {
95279               return this.geometryFactory.createMultiPolygon()
95280             }
95281
95282             var polygon;
95283             var polygons = str.trim().split(regExes.doubleParenComma);
95284             var components = [];
95285             for (var i = 0, len = polygons.length; i < len; ++i) {
95286               polygon = polygons[i].replace(regExes.trimParens, '$1');
95287               components.push(parse$1.polygon.apply(this$1, [polygon]));
95288             }
95289             return this.geometryFactory.createMultiPolygon(components)
95290           },
95291
95292           /**
95293            * Return a geometrycollection given a geometrycollection WKT fragment.
95294            *
95295            * @param {String} str A WKT fragment representing the geometrycollection.
95296            * @return {GeometryCollection}
95297            * @private
95298            */
95299           geometrycollection: function geometrycollection (str) {
95300             var this$1 = this;
95301
95302             if (str === undefined) {
95303               return this.geometryFactory.createGeometryCollection()
95304             }
95305
95306             // separate components of the collection with |
95307             str = str.replace(/,\s*([A-Za-z])/g, '|$1');
95308             var wktArray = str.trim().split('|');
95309             var components = [];
95310             for (var i = 0, len = wktArray.length; i < len; ++i) {
95311               components.push(this$1.read(wktArray[i]));
95312             }
95313             return this.geometryFactory.createGeometryCollection(components)
95314           }
95315         };
95316
95317         /**
95318          * Writes the Well-Known Text representation of a {@link Geometry}. The
95319          * Well-Known Text format is defined in the <A
95320          * HREF="http://www.opengis.org/techno/specs.htm"> OGC Simple Features
95321          * Specification for SQL</A>.
95322          * <p>
95323          * The <code>WKTWriter</code> outputs coordinates rounded to the precision
95324          * model. Only the maximum number of decimal places necessary to represent the
95325          * ordinates to the required precision will be output.
95326          * <p>
95327          * The SFS WKT spec does not define a special tag for {@link LinearRing}s.
95328          * Under the spec, rings are output as <code>LINESTRING</code>s.
95329          */
95330
95331         /**
95332          * @param {GeometryFactory} geometryFactory
95333          * @constructor
95334          */
95335         var WKTWriter = function WKTWriter (geometryFactory) {
95336           this.parser = new WKTParser(geometryFactory);
95337         };
95338
95339         /**
95340          * Converts a <code>Geometry</code> to its Well-known Text representation.
95341          *
95342          * @param {Geometry} geometry a <code>Geometry</code> to process.
95343          * @return {string} a <Geometry Tagged Text> string (see the OpenGIS Simple
95344          *       Features Specification).
95345          * @memberof WKTWriter
95346          */
95347         WKTWriter.prototype.write = function write (geometry) {
95348           return this.parser.write(geometry)
95349         };
95350         /**
95351          * Generates the WKT for a <tt>LINESTRING</tt> specified by two
95352          * {@link Coordinate}s.
95353          *
95354          * @param p0 the first coordinate.
95355          * @param p1 the second coordinate.
95356          *
95357          * @return the WKT.
95358          * @private
95359          */
95360         WKTWriter.toLineString = function toLineString (p0, p1) {
95361           if (arguments.length !== 2) {
95362             throw new Error('Not implemented')
95363           }
95364           return 'LINESTRING ( ' + p0.x + ' ' + p0.y + ', ' + p1.x + ' ' + p1.y + ' )'
95365         };
95366
95367         var RuntimeException = (function (Error) {
95368           function RuntimeException (message) {
95369             Error.call(this, message);
95370             this.name = 'RuntimeException';
95371             this.message = message;
95372             this.stack = (new Error()).stack;
95373           }
95374
95375           if ( Error ) { RuntimeException.__proto__ = Error; }
95376           RuntimeException.prototype = Object.create( Error && Error.prototype );
95377           RuntimeException.prototype.constructor = RuntimeException;
95378
95379           return RuntimeException;
95380         }(Error));
95381
95382         var AssertionFailedException = (function (RuntimeException$$1) {
95383           function AssertionFailedException () {
95384             RuntimeException$$1.call(this);
95385             if (arguments.length === 0) {
95386               RuntimeException$$1.call(this);
95387             } else if (arguments.length === 1) {
95388               var message = arguments[0];
95389               RuntimeException$$1.call(this, message);
95390             }
95391           }
95392
95393           if ( RuntimeException$$1 ) { AssertionFailedException.__proto__ = RuntimeException$$1; }
95394           AssertionFailedException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
95395           AssertionFailedException.prototype.constructor = AssertionFailedException;
95396           AssertionFailedException.prototype.interfaces_ = function interfaces_ () {
95397             return []
95398           };
95399           AssertionFailedException.prototype.getClass = function getClass () {
95400             return AssertionFailedException
95401           };
95402
95403           return AssertionFailedException;
95404         }(RuntimeException));
95405
95406         var Assert = function Assert () {};
95407
95408         Assert.prototype.interfaces_ = function interfaces_ () {
95409           return []
95410         };
95411         Assert.prototype.getClass = function getClass () {
95412           return Assert
95413         };
95414         Assert.shouldNeverReachHere = function shouldNeverReachHere () {
95415           if (arguments.length === 0) {
95416             Assert.shouldNeverReachHere(null);
95417           } else if (arguments.length === 1) {
95418             var message = arguments[0];
95419             throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : ''))
95420           }
95421         };
95422         Assert.isTrue = function isTrue () {
95423           var assertion;
95424           var message;
95425           if (arguments.length === 1) {
95426             assertion = arguments[0];
95427             Assert.isTrue(assertion, null);
95428           } else if (arguments.length === 2) {
95429             assertion = arguments[0];
95430             message = arguments[1];
95431             if (!assertion) {
95432               if (message === null) {
95433                 throw new AssertionFailedException()
95434               } else {
95435                 throw new AssertionFailedException(message)
95436               }
95437             }
95438           }
95439         };
95440         Assert.equals = function equals () {
95441           var expectedValue;
95442           var actualValue;
95443           var message;
95444           if (arguments.length === 2) {
95445             expectedValue = arguments[0];
95446             actualValue = arguments[1];
95447             Assert.equals(expectedValue, actualValue, null);
95448           } else if (arguments.length === 3) {
95449             expectedValue = arguments[0];
95450             actualValue = arguments[1];
95451             message = arguments[2];
95452             if (!actualValue.equals(expectedValue)) {
95453               throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : ''))
95454             }
95455           }
95456         };
95457
95458         var LineIntersector = function LineIntersector () {
95459           this._result = null;
95460           this._inputLines = Array(2).fill().map(function () { return Array(2); });
95461           this._intPt = new Array(2).fill(null);
95462           this._intLineIndex = null;
95463           this._isProper = null;
95464           this._pa = null;
95465           this._pb = null;
95466           this._precisionModel = null;
95467           this._intPt[0] = new Coordinate();
95468           this._intPt[1] = new Coordinate();
95469           this._pa = this._intPt[0];
95470           this._pb = this._intPt[1];
95471           this._result = 0;
95472         };
95473
95474         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 } };
95475         LineIntersector.prototype.getIndexAlongSegment = function getIndexAlongSegment (segmentIndex, intIndex) {
95476           this.computeIntLineIndex();
95477           return this._intLineIndex[segmentIndex][intIndex]
95478         };
95479         LineIntersector.prototype.getTopologySummary = function getTopologySummary () {
95480           var catBuf = new StringBuffer();
95481           if (this.isEndPoint()) { catBuf.append(' endpoint'); }
95482           if (this._isProper) { catBuf.append(' proper'); }
95483           if (this.isCollinear()) { catBuf.append(' collinear'); }
95484           return catBuf.toString()
95485         };
95486         LineIntersector.prototype.computeIntersection = function computeIntersection (p1, p2, p3, p4) {
95487           this._inputLines[0][0] = p1;
95488           this._inputLines[0][1] = p2;
95489           this._inputLines[1][0] = p3;
95490           this._inputLines[1][1] = p4;
95491           this._result = this.computeIntersect(p1, p2, p3, p4);
95492         };
95493         LineIntersector.prototype.getIntersectionNum = function getIntersectionNum () {
95494           return this._result
95495         };
95496         LineIntersector.prototype.computeIntLineIndex = function computeIntLineIndex () {
95497           if (arguments.length === 0) {
95498             if (this._intLineIndex === null) {
95499               this._intLineIndex = Array(2).fill().map(function () { return Array(2); });
95500               this.computeIntLineIndex(0);
95501               this.computeIntLineIndex(1);
95502             }
95503           } else if (arguments.length === 1) {
95504             var segmentIndex = arguments[0];
95505             var dist0 = this.getEdgeDistance(segmentIndex, 0);
95506             var dist1 = this.getEdgeDistance(segmentIndex, 1);
95507             if (dist0 > dist1) {
95508               this._intLineIndex[segmentIndex][0] = 0;
95509               this._intLineIndex[segmentIndex][1] = 1;
95510             } else {
95511               this._intLineIndex[segmentIndex][0] = 1;
95512               this._intLineIndex[segmentIndex][1] = 0;
95513             }
95514           }
95515         };
95516         LineIntersector.prototype.isProper = function isProper () {
95517           return this.hasIntersection() && this._isProper
95518         };
95519         LineIntersector.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
95520           this._precisionModel = precisionModel;
95521         };
95522         LineIntersector.prototype.isInteriorIntersection = function isInteriorIntersection () {
95523             var this$1 = this;
95524
95525           if (arguments.length === 0) {
95526             if (this.isInteriorIntersection(0)) { return true }
95527             if (this.isInteriorIntersection(1)) { return true }
95528             return false
95529           } else if (arguments.length === 1) {
95530             var inputLineIndex = arguments[0];
95531             for (var i = 0; i < this._result; i++) {
95532               if (!(this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][0]) || this$1._intPt[i].equals2D(this$1._inputLines[inputLineIndex][1]))) {
95533                 return true
95534               }
95535             }
95536             return false
95537           }
95538         };
95539         LineIntersector.prototype.getIntersection = function getIntersection (intIndex) {
95540           return this._intPt[intIndex]
95541         };
95542         LineIntersector.prototype.isEndPoint = function isEndPoint () {
95543           return this.hasIntersection() && !this._isProper
95544         };
95545         LineIntersector.prototype.hasIntersection = function hasIntersection () {
95546           return this._result !== LineIntersector.NO_INTERSECTION
95547         };
95548         LineIntersector.prototype.getEdgeDistance = function getEdgeDistance (segmentIndex, intIndex) {
95549           var dist = LineIntersector.computeEdgeDistance(this._intPt[intIndex], this._inputLines[segmentIndex][0], this._inputLines[segmentIndex][1]);
95550           return dist
95551         };
95552         LineIntersector.prototype.isCollinear = function isCollinear () {
95553           return this._result === LineIntersector.COLLINEAR_INTERSECTION
95554         };
95555         LineIntersector.prototype.toString = function toString () {
95556           return WKTWriter.toLineString(this._inputLines[0][0], this._inputLines[0][1]) + ' - ' + WKTWriter.toLineString(this._inputLines[1][0], this._inputLines[1][1]) + this.getTopologySummary()
95557         };
95558         LineIntersector.prototype.getEndpoint = function getEndpoint (segmentIndex, ptIndex) {
95559           return this._inputLines[segmentIndex][ptIndex]
95560         };
95561         LineIntersector.prototype.isIntersection = function isIntersection (pt) {
95562             var this$1 = this;
95563
95564           for (var i = 0; i < this._result; i++) {
95565             if (this$1._intPt[i].equals2D(pt)) {
95566               return true
95567             }
95568           }
95569           return false
95570         };
95571         LineIntersector.prototype.getIntersectionAlongSegment = function getIntersectionAlongSegment (segmentIndex, intIndex) {
95572           this.computeIntLineIndex();
95573           return this._intPt[this._intLineIndex[segmentIndex][intIndex]]
95574         };
95575         LineIntersector.prototype.interfaces_ = function interfaces_ () {
95576           return []
95577         };
95578         LineIntersector.prototype.getClass = function getClass () {
95579           return LineIntersector
95580         };
95581         LineIntersector.computeEdgeDistance = function computeEdgeDistance (p, p0, p1) {
95582           var dx = Math.abs(p1.x - p0.x);
95583           var dy = Math.abs(p1.y - p0.y);
95584           var dist = -1.0;
95585           if (p.equals(p0)) {
95586             dist = 0.0;
95587           } else if (p.equals(p1)) {
95588             if (dx > dy) { dist = dx; } else { dist = dy; }
95589           } else {
95590             var pdx = Math.abs(p.x - p0.x);
95591             var pdy = Math.abs(p.y - p0.y);
95592             if (dx > dy) { dist = pdx; } else { dist = pdy; }
95593             if (dist === 0.0 && !p.equals(p0)) {
95594               dist = Math.max(pdx, pdy);
95595             }
95596           }
95597           Assert.isTrue(!(dist === 0.0 && !p.equals(p0)), 'Bad distance calculation');
95598           return dist
95599         };
95600         LineIntersector.nonRobustComputeEdgeDistance = function nonRobustComputeEdgeDistance (p, p1, p2) {
95601           var dx = p.x - p1.x;
95602           var dy = p.y - p1.y;
95603           var dist = Math.sqrt(dx * dx + dy * dy);
95604           Assert.isTrue(!(dist === 0.0 && !p.equals(p1)), 'Invalid distance calculation');
95605           return dist
95606         };
95607         staticAccessors$10.DONT_INTERSECT.get = function () { return 0 };
95608         staticAccessors$10.DO_INTERSECT.get = function () { return 1 };
95609         staticAccessors$10.COLLINEAR.get = function () { return 2 };
95610         staticAccessors$10.NO_INTERSECTION.get = function () { return 0 };
95611         staticAccessors$10.POINT_INTERSECTION.get = function () { return 1 };
95612         staticAccessors$10.COLLINEAR_INTERSECTION.get = function () { return 2 };
95613
95614         Object.defineProperties( LineIntersector, staticAccessors$10 );
95615
95616         var RobustLineIntersector = (function (LineIntersector$$1) {
95617           function RobustLineIntersector () {
95618             LineIntersector$$1.apply(this, arguments);
95619           }
95620
95621           if ( LineIntersector$$1 ) { RobustLineIntersector.__proto__ = LineIntersector$$1; }
95622           RobustLineIntersector.prototype = Object.create( LineIntersector$$1 && LineIntersector$$1.prototype );
95623           RobustLineIntersector.prototype.constructor = RobustLineIntersector;
95624
95625           RobustLineIntersector.prototype.isInSegmentEnvelopes = function isInSegmentEnvelopes (intPt) {
95626             var env0 = new Envelope(this._inputLines[0][0], this._inputLines[0][1]);
95627             var env1 = new Envelope(this._inputLines[1][0], this._inputLines[1][1]);
95628             return env0.contains(intPt) && env1.contains(intPt)
95629           };
95630           RobustLineIntersector.prototype.computeIntersection = function computeIntersection () {
95631             if (arguments.length === 3) {
95632               var p = arguments[0];
95633               var p1 = arguments[1];
95634               var p2 = arguments[2];
95635               this._isProper = false;
95636               if (Envelope.intersects(p1, p2, p)) {
95637                 if (CGAlgorithms.orientationIndex(p1, p2, p) === 0 && CGAlgorithms.orientationIndex(p2, p1, p) === 0) {
95638                   this._isProper = true;
95639                   if (p.equals(p1) || p.equals(p2)) {
95640                     this._isProper = false;
95641                   }
95642                   this._result = LineIntersector$$1.POINT_INTERSECTION;
95643                   return null
95644                 }
95645               }
95646               this._result = LineIntersector$$1.NO_INTERSECTION;
95647             } else { return LineIntersector$$1.prototype.computeIntersection.apply(this, arguments) }
95648           };
95649           RobustLineIntersector.prototype.normalizeToMinimum = function normalizeToMinimum (n1, n2, n3, n4, normPt) {
95650             normPt.x = this.smallestInAbsValue(n1.x, n2.x, n3.x, n4.x);
95651             normPt.y = this.smallestInAbsValue(n1.y, n2.y, n3.y, n4.y);
95652             n1.x -= normPt.x;
95653             n1.y -= normPt.y;
95654             n2.x -= normPt.x;
95655             n2.y -= normPt.y;
95656             n3.x -= normPt.x;
95657             n3.y -= normPt.y;
95658             n4.x -= normPt.x;
95659             n4.y -= normPt.y;
95660           };
95661           RobustLineIntersector.prototype.safeHCoordinateIntersection = function safeHCoordinateIntersection (p1, p2, q1, q2) {
95662             var intPt = null;
95663             try {
95664               intPt = HCoordinate.intersection(p1, p2, q1, q2);
95665             } catch (e) {
95666               if (e instanceof NotRepresentableException) {
95667                 intPt = RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2);
95668               } else { throw e }
95669             } finally {}
95670             return intPt
95671           };
95672           RobustLineIntersector.prototype.intersection = function intersection (p1, p2, q1, q2) {
95673             var intPt = this.intersectionWithNormalization(p1, p2, q1, q2);
95674             if (!this.isInSegmentEnvelopes(intPt)) {
95675               intPt = new Coordinate(RobustLineIntersector.nearestEndpoint(p1, p2, q1, q2));
95676             }
95677             if (this._precisionModel !== null) {
95678               this._precisionModel.makePrecise(intPt);
95679             }
95680             return intPt
95681           };
95682           RobustLineIntersector.prototype.smallestInAbsValue = function smallestInAbsValue (x1, x2, x3, x4) {
95683             var x = x1;
95684             var xabs = Math.abs(x);
95685             if (Math.abs(x2) < xabs) {
95686               x = x2;
95687               xabs = Math.abs(x2);
95688             }
95689             if (Math.abs(x3) < xabs) {
95690               x = x3;
95691               xabs = Math.abs(x3);
95692             }
95693             if (Math.abs(x4) < xabs) {
95694               x = x4;
95695             }
95696             return x
95697           };
95698           RobustLineIntersector.prototype.checkDD = function checkDD (p1, p2, q1, q2, intPt) {
95699             var intPtDD = CGAlgorithmsDD.intersection(p1, p2, q1, q2);
95700             var isIn = this.isInSegmentEnvelopes(intPtDD);
95701             System.out.println('DD in env = ' + isIn + '  --------------------- ' + intPtDD);
95702             if (intPt.distance(intPtDD) > 0.0001) {
95703               System.out.println('Distance = ' + intPt.distance(intPtDD));
95704             }
95705           };
95706           RobustLineIntersector.prototype.intersectionWithNormalization = function intersectionWithNormalization (p1, p2, q1, q2) {
95707             var n1 = new Coordinate(p1);
95708             var n2 = new Coordinate(p2);
95709             var n3 = new Coordinate(q1);
95710             var n4 = new Coordinate(q2);
95711             var normPt = new Coordinate();
95712             this.normalizeToEnvCentre(n1, n2, n3, n4, normPt);
95713             var intPt = this.safeHCoordinateIntersection(n1, n2, n3, n4);
95714             intPt.x += normPt.x;
95715             intPt.y += normPt.y;
95716             return intPt
95717           };
95718           RobustLineIntersector.prototype.computeCollinearIntersection = function computeCollinearIntersection (p1, p2, q1, q2) {
95719             var p1q1p2 = Envelope.intersects(p1, p2, q1);
95720             var p1q2p2 = Envelope.intersects(p1, p2, q2);
95721             var q1p1q2 = Envelope.intersects(q1, q2, p1);
95722             var q1p2q2 = Envelope.intersects(q1, q2, p2);
95723             if (p1q1p2 && p1q2p2) {
95724               this._intPt[0] = q1;
95725               this._intPt[1] = q2;
95726               return LineIntersector$$1.COLLINEAR_INTERSECTION
95727             }
95728             if (q1p1q2 && q1p2q2) {
95729               this._intPt[0] = p1;
95730               this._intPt[1] = p2;
95731               return LineIntersector$$1.COLLINEAR_INTERSECTION
95732             }
95733             if (p1q1p2 && q1p1q2) {
95734               this._intPt[0] = q1;
95735               this._intPt[1] = p1;
95736               return q1.equals(p1) && !p1q2p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95737             }
95738             if (p1q1p2 && q1p2q2) {
95739               this._intPt[0] = q1;
95740               this._intPt[1] = p2;
95741               return q1.equals(p2) && !p1q2p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95742             }
95743             if (p1q2p2 && q1p1q2) {
95744               this._intPt[0] = q2;
95745               this._intPt[1] = p1;
95746               return q2.equals(p1) && !p1q1p2 && !q1p2q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95747             }
95748             if (p1q2p2 && q1p2q2) {
95749               this._intPt[0] = q2;
95750               this._intPt[1] = p2;
95751               return q2.equals(p2) && !p1q1p2 && !q1p1q2 ? LineIntersector$$1.POINT_INTERSECTION : LineIntersector$$1.COLLINEAR_INTERSECTION
95752             }
95753             return LineIntersector$$1.NO_INTERSECTION
95754           };
95755           RobustLineIntersector.prototype.normalizeToEnvCentre = function normalizeToEnvCentre (n00, n01, n10, n11, normPt) {
95756             var minX0 = n00.x < n01.x ? n00.x : n01.x;
95757             var minY0 = n00.y < n01.y ? n00.y : n01.y;
95758             var maxX0 = n00.x > n01.x ? n00.x : n01.x;
95759             var maxY0 = n00.y > n01.y ? n00.y : n01.y;
95760             var minX1 = n10.x < n11.x ? n10.x : n11.x;
95761             var minY1 = n10.y < n11.y ? n10.y : n11.y;
95762             var maxX1 = n10.x > n11.x ? n10.x : n11.x;
95763             var maxY1 = n10.y > n11.y ? n10.y : n11.y;
95764             var intMinX = minX0 > minX1 ? minX0 : minX1;
95765             var intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
95766             var intMinY = minY0 > minY1 ? minY0 : minY1;
95767             var intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;
95768             var intMidX = (intMinX + intMaxX) / 2.0;
95769             var intMidY = (intMinY + intMaxY) / 2.0;
95770             normPt.x = intMidX;
95771             normPt.y = intMidY;
95772             n00.x -= normPt.x;
95773             n00.y -= normPt.y;
95774             n01.x -= normPt.x;
95775             n01.y -= normPt.y;
95776             n10.x -= normPt.x;
95777             n10.y -= normPt.y;
95778             n11.x -= normPt.x;
95779             n11.y -= normPt.y;
95780           };
95781           RobustLineIntersector.prototype.computeIntersect = function computeIntersect (p1, p2, q1, q2) {
95782             this._isProper = false;
95783             if (!Envelope.intersects(p1, p2, q1, q2)) { return LineIntersector$$1.NO_INTERSECTION }
95784             var Pq1 = CGAlgorithms.orientationIndex(p1, p2, q1);
95785             var Pq2 = CGAlgorithms.orientationIndex(p1, p2, q2);
95786             if ((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
95787               return LineIntersector$$1.NO_INTERSECTION
95788             }
95789             var Qp1 = CGAlgorithms.orientationIndex(q1, q2, p1);
95790             var Qp2 = CGAlgorithms.orientationIndex(q1, q2, p2);
95791             if ((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
95792               return LineIntersector$$1.NO_INTERSECTION
95793             }
95794             var collinear = Pq1 === 0 && Pq2 === 0 && Qp1 === 0 && Qp2 === 0;
95795             if (collinear) {
95796               return this.computeCollinearIntersection(p1, p2, q1, q2)
95797             }
95798             if (Pq1 === 0 || Pq2 === 0 || Qp1 === 0 || Qp2 === 0) {
95799               this._isProper = false;
95800               if (p1.equals2D(q1) || p1.equals2D(q2)) {
95801                 this._intPt[0] = p1;
95802               } else if (p2.equals2D(q1) || p2.equals2D(q2)) {
95803                 this._intPt[0] = p2;
95804               } else if (Pq1 === 0) {
95805                 this._intPt[0] = new Coordinate(q1);
95806               } else if (Pq2 === 0) {
95807                 this._intPt[0] = new Coordinate(q2);
95808               } else if (Qp1 === 0) {
95809                 this._intPt[0] = new Coordinate(p1);
95810               } else if (Qp2 === 0) {
95811                 this._intPt[0] = new Coordinate(p2);
95812               }
95813             } else {
95814               this._isProper = true;
95815               this._intPt[0] = this.intersection(p1, p2, q1, q2);
95816             }
95817             return LineIntersector$$1.POINT_INTERSECTION
95818           };
95819           RobustLineIntersector.prototype.interfaces_ = function interfaces_ () {
95820             return []
95821           };
95822           RobustLineIntersector.prototype.getClass = function getClass () {
95823             return RobustLineIntersector
95824           };
95825           RobustLineIntersector.nearestEndpoint = function nearestEndpoint (p1, p2, q1, q2) {
95826             var nearestPt = p1;
95827             var minDist = CGAlgorithms.distancePointLine(p1, q1, q2);
95828             var dist = CGAlgorithms.distancePointLine(p2, q1, q2);
95829             if (dist < minDist) {
95830               minDist = dist;
95831               nearestPt = p2;
95832             }
95833             dist = CGAlgorithms.distancePointLine(q1, p1, p2);
95834             if (dist < minDist) {
95835               minDist = dist;
95836               nearestPt = q1;
95837             }
95838             dist = CGAlgorithms.distancePointLine(q2, p1, p2);
95839             if (dist < minDist) {
95840               minDist = dist;
95841               nearestPt = q2;
95842             }
95843             return nearestPt
95844           };
95845
95846           return RobustLineIntersector;
95847         }(LineIntersector));
95848
95849         var RobustDeterminant = function RobustDeterminant () {};
95850
95851         RobustDeterminant.prototype.interfaces_ = function interfaces_ () {
95852           return []
95853         };
95854         RobustDeterminant.prototype.getClass = function getClass () {
95855           return RobustDeterminant
95856         };
95857         RobustDeterminant.orientationIndex = function orientationIndex (p1, p2, q) {
95858           var dx1 = p2.x - p1.x;
95859           var dy1 = p2.y - p1.y;
95860           var dx2 = q.x - p2.x;
95861           var dy2 = q.y - p2.y;
95862           return RobustDeterminant.signOfDet2x2(dx1, dy1, dx2, dy2)
95863         };
95864         RobustDeterminant.signOfDet2x2 = function signOfDet2x2 (x1, y1, x2, y2) {
95865           var sign = null;
95866           var swap = null;
95867           var k = null;
95868           sign = 1;
95869           if (x1 === 0.0 || y2 === 0.0) {
95870             if (y1 === 0.0 || x2 === 0.0) {
95871               return 0
95872             } else if (y1 > 0) {
95873               if (x2 > 0) {
95874                 return -sign
95875               } else {
95876                 return sign
95877               }
95878             } else {
95879               if (x2 > 0) {
95880                 return sign
95881               } else {
95882                 return -sign
95883               }
95884             }
95885           }
95886           if (y1 === 0.0 || x2 === 0.0) {
95887             if (y2 > 0) {
95888               if (x1 > 0) {
95889                 return sign
95890               } else {
95891                 return -sign
95892               }
95893             } else {
95894               if (x1 > 0) {
95895                 return -sign
95896               } else {
95897                 return sign
95898               }
95899             }
95900           }
95901           if (y1 > 0.0) {
95902             if (y2 > 0.0) {
95903               if (y1 <= y2) ; else {
95904                 sign = -sign;
95905                 swap = x1;
95906                 x1 = x2;
95907                 x2 = swap;
95908                 swap = y1;
95909                 y1 = y2;
95910                 y2 = swap;
95911               }
95912             } else {
95913               if (y1 <= -y2) {
95914                 sign = -sign;
95915                 x2 = -x2;
95916                 y2 = -y2;
95917               } else {
95918                 swap = x1;
95919                 x1 = -x2;
95920                 x2 = swap;
95921                 swap = y1;
95922                 y1 = -y2;
95923                 y2 = swap;
95924               }
95925             }
95926           } else {
95927             if (y2 > 0.0) {
95928               if (-y1 <= y2) {
95929                 sign = -sign;
95930                 x1 = -x1;
95931                 y1 = -y1;
95932               } else {
95933                 swap = -x1;
95934                 x1 = x2;
95935                 x2 = swap;
95936                 swap = -y1;
95937                 y1 = y2;
95938                 y2 = swap;
95939               }
95940             } else {
95941               if (y1 >= y2) {
95942                 x1 = -x1;
95943                 y1 = -y1;
95944                 x2 = -x2;
95945                 y2 = -y2;
95946               } else {
95947                 sign = -sign;
95948                 swap = -x1;
95949                 x1 = -x2;
95950                 x2 = swap;
95951                 swap = -y1;
95952                 y1 = -y2;
95953                 y2 = swap;
95954               }
95955             }
95956           }
95957           if (x1 > 0.0) {
95958             if (x2 > 0.0) {
95959               if (x1 <= x2) ; else {
95960                 return sign
95961               }
95962             } else {
95963               return sign
95964             }
95965           } else {
95966             if (x2 > 0.0) {
95967               return -sign
95968             } else {
95969               if (x1 >= x2) {
95970                 sign = -sign;
95971                 x1 = -x1;
95972                 x2 = -x2;
95973               } else {
95974                 return -sign
95975               }
95976             }
95977           }
95978           while (true) {
95979             k = Math.floor(x2 / x1);
95980             x2 = x2 - k * x1;
95981             y2 = y2 - k * y1;
95982             if (y2 < 0.0) {
95983               return -sign
95984             }
95985             if (y2 > y1) {
95986               return sign
95987             }
95988             if (x1 > x2 + x2) {
95989               if (y1 < y2 + y2) {
95990                 return sign
95991               }
95992             } else {
95993               if (y1 > y2 + y2) {
95994                 return -sign
95995               } else {
95996                 x2 = x1 - x2;
95997                 y2 = y1 - y2;
95998                 sign = -sign;
95999               }
96000             }
96001             if (y2 === 0.0) {
96002               if (x2 === 0.0) {
96003                 return 0
96004               } else {
96005                 return -sign
96006               }
96007             }
96008             if (x2 === 0.0) {
96009               return sign
96010             }
96011             k = Math.floor(x1 / x2);
96012             x1 = x1 - k * x2;
96013             y1 = y1 - k * y2;
96014             if (y1 < 0.0) {
96015               return sign
96016             }
96017             if (y1 > y2) {
96018               return -sign
96019             }
96020             if (x2 > x1 + x1) {
96021               if (y2 < y1 + y1) {
96022                 return -sign
96023               }
96024             } else {
96025               if (y2 > y1 + y1) {
96026                 return sign
96027               } else {
96028                 x1 = x2 - x1;
96029                 y1 = y2 - y1;
96030                 sign = -sign;
96031               }
96032             }
96033             if (y1 === 0.0) {
96034               if (x1 === 0.0) {
96035                 return 0
96036               } else {
96037                 return sign
96038               }
96039             }
96040             if (x1 === 0.0) {
96041               return -sign
96042             }
96043           }
96044         };
96045
96046         var RayCrossingCounter = function RayCrossingCounter () {
96047           this._p = null;
96048           this._crossingCount = 0;
96049           this._isPointOnSegment = false;
96050           var p = arguments[0];
96051           this._p = p;
96052         };
96053         RayCrossingCounter.prototype.countSegment = function countSegment (p1, p2) {
96054           if (p1.x < this._p.x && p2.x < this._p.x) { return null }
96055           if (this._p.x === p2.x && this._p.y === p2.y) {
96056             this._isPointOnSegment = true;
96057             return null
96058           }
96059           if (p1.y === this._p.y && p2.y === this._p.y) {
96060             var minx = p1.x;
96061             var maxx = p2.x;
96062             if (minx > maxx) {
96063               minx = p2.x;
96064               maxx = p1.x;
96065             }
96066             if (this._p.x >= minx && this._p.x <= maxx) {
96067               this._isPointOnSegment = true;
96068             }
96069             return null
96070           }
96071           if ((p1.y > this._p.y && p2.y <= this._p.y) || (p2.y > this._p.y && p1.y <= this._p.y)) {
96072             var x1 = p1.x - this._p.x;
96073             var y1 = p1.y - this._p.y;
96074             var x2 = p2.x - this._p.x;
96075             var y2 = p2.y - this._p.y;
96076             var xIntSign = RobustDeterminant.signOfDet2x2(x1, y1, x2, y2);
96077             if (xIntSign === 0.0) {
96078               this._isPointOnSegment = true;
96079               return null
96080             }
96081             if (y2 < y1) { xIntSign = -xIntSign; }
96082             if (xIntSign > 0.0) {
96083               this._crossingCount++;
96084             }
96085           }
96086         };
96087         RayCrossingCounter.prototype.isPointInPolygon = function isPointInPolygon () {
96088           return this.getLocation() !== Location.EXTERIOR
96089         };
96090         RayCrossingCounter.prototype.getLocation = function getLocation () {
96091           if (this._isPointOnSegment) { return Location.BOUNDARY }
96092           if (this._crossingCount % 2 === 1) {
96093             return Location.INTERIOR
96094           }
96095           return Location.EXTERIOR
96096         };
96097         RayCrossingCounter.prototype.isOnSegment = function isOnSegment () {
96098           return this._isPointOnSegment
96099         };
96100         RayCrossingCounter.prototype.interfaces_ = function interfaces_ () {
96101           return []
96102         };
96103         RayCrossingCounter.prototype.getClass = function getClass () {
96104           return RayCrossingCounter
96105         };
96106         RayCrossingCounter.locatePointInRing = function locatePointInRing () {
96107           if (arguments[0] instanceof Coordinate && hasInterface(arguments[1], CoordinateSequence)) {
96108             var p = arguments[0];
96109             var ring = arguments[1];
96110             var counter = new RayCrossingCounter(p);
96111             var p1 = new Coordinate();
96112             var p2 = new Coordinate();
96113             for (var i = 1; i < ring.size(); i++) {
96114               ring.getCoordinate(i, p1);
96115               ring.getCoordinate(i - 1, p2);
96116               counter.countSegment(p1, p2);
96117               if (counter.isOnSegment()) { return counter.getLocation() }
96118             }
96119             return counter.getLocation()
96120           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Array) {
96121             var p$1 = arguments[0];
96122             var ring$1 = arguments[1];
96123             var counter$1 = new RayCrossingCounter(p$1);
96124             for (var i$1 = 1; i$1 < ring$1.length; i$1++) {
96125               var p1$1 = ring$1[i$1];
96126               var p2$1 = ring$1[i$1 - 1];
96127               counter$1.countSegment(p1$1, p2$1);
96128               if (counter$1.isOnSegment()) { return counter$1.getLocation() }
96129             }
96130             return counter$1.getLocation()
96131           }
96132         };
96133
96134         var CGAlgorithms = function CGAlgorithms () {};
96135
96136         var staticAccessors$3 = { CLOCKWISE: { configurable: true },RIGHT: { configurable: true },COUNTERCLOCKWISE: { configurable: true },LEFT: { configurable: true },COLLINEAR: { configurable: true },STRAIGHT: { configurable: true } };
96137
96138         CGAlgorithms.prototype.interfaces_ = function interfaces_ () {
96139           return []
96140         };
96141         CGAlgorithms.prototype.getClass = function getClass () {
96142           return CGAlgorithms
96143         };
96144         CGAlgorithms.orientationIndex = function orientationIndex (p1, p2, q) {
96145           return CGAlgorithmsDD.orientationIndex(p1, p2, q)
96146         };
96147         CGAlgorithms.signedArea = function signedArea () {
96148           if (arguments[0] instanceof Array) {
96149             var ring = arguments[0];
96150             if (ring.length < 3) { return 0.0 }
96151             var sum = 0.0;
96152             var x0 = ring[0].x;
96153             for (var i = 1; i < ring.length - 1; i++) {
96154               var x = ring[i].x - x0;
96155               var y1 = ring[i + 1].y;
96156               var y2 = ring[i - 1].y;
96157               sum += x * (y2 - y1);
96158             }
96159             return sum / 2.0
96160           } else if (hasInterface(arguments[0], CoordinateSequence)) {
96161             var ring$1 = arguments[0];
96162             var n = ring$1.size();
96163             if (n < 3) { return 0.0 }
96164             var p0 = new Coordinate();
96165             var p1 = new Coordinate();
96166             var p2 = new Coordinate();
96167             ring$1.getCoordinate(0, p1);
96168             ring$1.getCoordinate(1, p2);
96169             var x0$1 = p1.x;
96170             p2.x -= x0$1;
96171             var sum$1 = 0.0;
96172             for (var i$1 = 1; i$1 < n - 1; i$1++) {
96173               p0.y = p1.y;
96174               p1.x = p2.x;
96175               p1.y = p2.y;
96176               ring$1.getCoordinate(i$1 + 1, p2);
96177               p2.x -= x0$1;
96178               sum$1 += p1.x * (p0.y - p2.y);
96179             }
96180             return sum$1 / 2.0
96181           }
96182         };
96183         CGAlgorithms.distanceLineLine = function distanceLineLine (A, B, C, D) {
96184           if (A.equals(B)) { return CGAlgorithms.distancePointLine(A, C, D) }
96185           if (C.equals(D)) { return CGAlgorithms.distancePointLine(D, A, B) }
96186           var noIntersection = false;
96187           if (!Envelope.intersects(A, B, C, D)) {
96188             noIntersection = true;
96189           } else {
96190             var denom = (B.x - A.x) * (D.y - C.y) - (B.y - A.y) * (D.x - C.x);
96191             if (denom === 0) {
96192               noIntersection = true;
96193             } else {
96194               var rNumb = (A.y - C.y) * (D.x - C.x) - (A.x - C.x) * (D.y - C.y);
96195               var sNum = (A.y - C.y) * (B.x - A.x) - (A.x - C.x) * (B.y - A.y);
96196               var s = sNum / denom;
96197               var r = rNumb / denom;
96198               if (r < 0 || r > 1 || s < 0 || s > 1) {
96199                 noIntersection = true;
96200               }
96201             }
96202           }
96203           if (noIntersection) {
96204             return MathUtil.min(CGAlgorithms.distancePointLine(A, C, D), CGAlgorithms.distancePointLine(B, C, D), CGAlgorithms.distancePointLine(C, A, B), CGAlgorithms.distancePointLine(D, A, B))
96205           }
96206           return 0.0
96207         };
96208         CGAlgorithms.isPointInRing = function isPointInRing (p, ring) {
96209           return CGAlgorithms.locatePointInRing(p, ring) !== Location.EXTERIOR
96210         };
96211         CGAlgorithms.computeLength = function computeLength (pts) {
96212           var n = pts.size();
96213           if (n <= 1) { return 0.0 }
96214           var len = 0.0;
96215           var p = new Coordinate();
96216           pts.getCoordinate(0, p);
96217           var x0 = p.x;
96218           var y0 = p.y;
96219           for (var i = 1; i < n; i++) {
96220             pts.getCoordinate(i, p);
96221             var x1 = p.x;
96222             var y1 = p.y;
96223             var dx = x1 - x0;
96224             var dy = y1 - y0;
96225             len += Math.sqrt(dx * dx + dy * dy);
96226             x0 = x1;
96227             y0 = y1;
96228           }
96229           return len
96230         };
96231         CGAlgorithms.isCCW = function isCCW (ring) {
96232           var nPts = ring.length - 1;
96233           if (nPts < 3) { throw new IllegalArgumentException('Ring has fewer than 4 points, so orientation cannot be determined') }
96234           var hiPt = ring[0];
96235           var hiIndex = 0;
96236           for (var i = 1; i <= nPts; i++) {
96237             var p = ring[i];
96238             if (p.y > hiPt.y) {
96239               hiPt = p;
96240               hiIndex = i;
96241             }
96242           }
96243           var iPrev = hiIndex;
96244           do {
96245             iPrev = iPrev - 1;
96246             if (iPrev < 0) { iPrev = nPts; }
96247           } while (ring[iPrev].equals2D(hiPt) && iPrev !== hiIndex)
96248           var iNext = hiIndex;
96249           do {
96250             iNext = (iNext + 1) % nPts;
96251           } while (ring[iNext].equals2D(hiPt) && iNext !== hiIndex)
96252           var prev = ring[iPrev];
96253           var next = ring[iNext];
96254           if (prev.equals2D(hiPt) || next.equals2D(hiPt) || prev.equals2D(next)) { return false }
96255           var disc = CGAlgorithms.computeOrientation(prev, hiPt, next);
96256           var isCCW = false;
96257           if (disc === 0) {
96258             isCCW = prev.x > next.x;
96259           } else {
96260             isCCW = disc > 0;
96261           }
96262           return isCCW
96263         };
96264         CGAlgorithms.locatePointInRing = function locatePointInRing (p, ring) {
96265           return RayCrossingCounter.locatePointInRing(p, ring)
96266         };
96267         CGAlgorithms.distancePointLinePerpendicular = function distancePointLinePerpendicular (p, A, B) {
96268           var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96269           var s = ((A.y - p.y) * (B.x - A.x) - (A.x - p.x) * (B.y - A.y)) / len2;
96270           return Math.abs(s) * Math.sqrt(len2)
96271         };
96272         CGAlgorithms.computeOrientation = function computeOrientation (p1, p2, q) {
96273           return CGAlgorithms.orientationIndex(p1, p2, q)
96274         };
96275         CGAlgorithms.distancePointLine = function distancePointLine () {
96276           if (arguments.length === 2) {
96277             var p = arguments[0];
96278             var line = arguments[1];
96279             if (line.length === 0) { throw new IllegalArgumentException('Line array must contain at least one vertex') }
96280             var minDistance = p.distance(line[0]);
96281             for (var i = 0; i < line.length - 1; i++) {
96282               var dist = CGAlgorithms.distancePointLine(p, line[i], line[i + 1]);
96283               if (dist < minDistance) {
96284                 minDistance = dist;
96285               }
96286             }
96287             return minDistance
96288           } else if (arguments.length === 3) {
96289             var p$1 = arguments[0];
96290             var A = arguments[1];
96291             var B = arguments[2];
96292             if (A.x === B.x && A.y === B.y) { return p$1.distance(A) }
96293             var len2 = (B.x - A.x) * (B.x - A.x) + (B.y - A.y) * (B.y - A.y);
96294             var r = ((p$1.x - A.x) * (B.x - A.x) + (p$1.y - A.y) * (B.y - A.y)) / len2;
96295             if (r <= 0.0) { return p$1.distance(A) }
96296             if (r >= 1.0) { return p$1.distance(B) }
96297             var s = ((A.y - p$1.y) * (B.x - A.x) - (A.x - p$1.x) * (B.y - A.y)) / len2;
96298             return Math.abs(s) * Math.sqrt(len2)
96299           }
96300         };
96301         CGAlgorithms.isOnLine = function isOnLine (p, pt) {
96302           var lineIntersector = new RobustLineIntersector();
96303           for (var i = 1; i < pt.length; i++) {
96304             var p0 = pt[i - 1];
96305             var p1 = pt[i];
96306             lineIntersector.computeIntersection(p, p0, p1);
96307             if (lineIntersector.hasIntersection()) {
96308               return true
96309             }
96310           }
96311           return false
96312         };
96313         staticAccessors$3.CLOCKWISE.get = function () { return -1 };
96314         staticAccessors$3.RIGHT.get = function () { return CGAlgorithms.CLOCKWISE };
96315         staticAccessors$3.COUNTERCLOCKWISE.get = function () { return 1 };
96316         staticAccessors$3.LEFT.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
96317         staticAccessors$3.COLLINEAR.get = function () { return 0 };
96318         staticAccessors$3.STRAIGHT.get = function () { return CGAlgorithms.COLLINEAR };
96319
96320         Object.defineProperties( CGAlgorithms, staticAccessors$3 );
96321
96322         var GeometryComponentFilter = function GeometryComponentFilter () {};
96323
96324         GeometryComponentFilter.prototype.filter = function filter (geom) {};
96325         GeometryComponentFilter.prototype.interfaces_ = function interfaces_ () {
96326           return []
96327         };
96328         GeometryComponentFilter.prototype.getClass = function getClass () {
96329           return GeometryComponentFilter
96330         };
96331
96332         var Geometry = function Geometry () {
96333           var factory = arguments[0];
96334
96335           this._envelope = null;
96336           this._factory = null;
96337           this._SRID = null;
96338           this._userData = null;
96339           this._factory = factory;
96340           this._SRID = factory.getSRID();
96341         };
96342
96343         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 } };
96344         Geometry.prototype.isGeometryCollection = function isGeometryCollection () {
96345           return this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION
96346         };
96347         Geometry.prototype.getFactory = function getFactory () {
96348           return this._factory
96349         };
96350         Geometry.prototype.getGeometryN = function getGeometryN (n) {
96351           return this
96352         };
96353         Geometry.prototype.getArea = function getArea () {
96354           return 0.0
96355         };
96356         Geometry.prototype.isRectangle = function isRectangle () {
96357           return false
96358         };
96359         Geometry.prototype.equals = function equals () {
96360           if (arguments[0] instanceof Geometry) {
96361             var g$1 = arguments[0];
96362             if (g$1 === null) { return false }
96363             return this.equalsTopo(g$1)
96364           } else if (arguments[0] instanceof Object) {
96365             var o = arguments[0];
96366             if (!(o instanceof Geometry)) { return false }
96367             var g = o;
96368             return this.equalsExact(g)
96369           }
96370         };
96371         Geometry.prototype.equalsExact = function equalsExact (other) {
96372           return this === other || this.equalsExact(other, 0)
96373         };
96374         Geometry.prototype.geometryChanged = function geometryChanged () {
96375           this.apply(Geometry.geometryChangedFilter);
96376         };
96377         Geometry.prototype.geometryChangedAction = function geometryChangedAction () {
96378           this._envelope = null;
96379         };
96380         Geometry.prototype.equalsNorm = function equalsNorm (g) {
96381           if (g === null) { return false }
96382           return this.norm().equalsExact(g.norm())
96383         };
96384         Geometry.prototype.getLength = function getLength () {
96385           return 0.0
96386         };
96387         Geometry.prototype.getNumGeometries = function getNumGeometries () {
96388           return 1
96389         };
96390         Geometry.prototype.compareTo = function compareTo () {
96391           if (arguments.length === 1) {
96392             var o = arguments[0];
96393             var other = o;
96394             if (this.getSortIndex() !== other.getSortIndex()) {
96395               return this.getSortIndex() - other.getSortIndex()
96396             }
96397             if (this.isEmpty() && other.isEmpty()) {
96398               return 0
96399             }
96400             if (this.isEmpty()) {
96401               return -1
96402             }
96403             if (other.isEmpty()) {
96404               return 1
96405             }
96406             return this.compareToSameClass(o)
96407           } else if (arguments.length === 2) {
96408             var other$1 = arguments[0];
96409             var comp = arguments[1];
96410             if (this.getSortIndex() !== other$1.getSortIndex()) {
96411               return this.getSortIndex() - other$1.getSortIndex()
96412             }
96413             if (this.isEmpty() && other$1.isEmpty()) {
96414               return 0
96415             }
96416             if (this.isEmpty()) {
96417               return -1
96418             }
96419             if (other$1.isEmpty()) {
96420               return 1
96421             }
96422             return this.compareToSameClass(other$1, comp)
96423           }
96424         };
96425         Geometry.prototype.getUserData = function getUserData () {
96426           return this._userData
96427         };
96428         Geometry.prototype.getSRID = function getSRID () {
96429           return this._SRID
96430         };
96431         Geometry.prototype.getEnvelope = function getEnvelope () {
96432           return this.getFactory().toGeometry(this.getEnvelopeInternal())
96433         };
96434         Geometry.prototype.checkNotGeometryCollection = function checkNotGeometryCollection (g) {
96435           if (g.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION) {
96436             throw new IllegalArgumentException('This method does not support GeometryCollection arguments')
96437           }
96438         };
96439         Geometry.prototype.equal = function equal (a, b, tolerance) {
96440           if (tolerance === 0) {
96441             return a.equals(b)
96442           }
96443           return a.distance(b) <= tolerance
96444         };
96445         Geometry.prototype.norm = function norm () {
96446           var copy = this.copy();
96447           copy.normalize();
96448           return copy
96449         };
96450         Geometry.prototype.getPrecisionModel = function getPrecisionModel () {
96451           return this._factory.getPrecisionModel()
96452         };
96453         Geometry.prototype.getEnvelopeInternal = function getEnvelopeInternal () {
96454           if (this._envelope === null) {
96455             this._envelope = this.computeEnvelopeInternal();
96456           }
96457           return new Envelope(this._envelope)
96458         };
96459         Geometry.prototype.setSRID = function setSRID (SRID) {
96460           this._SRID = SRID;
96461         };
96462         Geometry.prototype.setUserData = function setUserData (userData) {
96463           this._userData = userData;
96464         };
96465         Geometry.prototype.compare = function compare (a, b) {
96466           var i = a.iterator();
96467           var j = b.iterator();
96468           while (i.hasNext() && j.hasNext()) {
96469             var aElement = i.next();
96470             var bElement = j.next();
96471             var comparison = aElement.compareTo(bElement);
96472             if (comparison !== 0) {
96473               return comparison
96474             }
96475           }
96476           if (i.hasNext()) {
96477             return 1
96478           }
96479           if (j.hasNext()) {
96480             return -1
96481           }
96482           return 0
96483         };
96484         Geometry.prototype.hashCode = function hashCode () {
96485           return this.getEnvelopeInternal().hashCode()
96486         };
96487         Geometry.prototype.isGeometryCollectionOrDerived = function isGeometryCollectionOrDerived () {
96488           if (this.getSortIndex() === Geometry.SORTINDEX_GEOMETRYCOLLECTION || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOINT || this.getSortIndex() === Geometry.SORTINDEX_MULTILINESTRING || this.getSortIndex() === Geometry.SORTINDEX_MULTIPOLYGON) {
96489             return true
96490           }
96491           return false
96492         };
96493         Geometry.prototype.interfaces_ = function interfaces_ () {
96494           return [Clonable, Comparable, Serializable]
96495         };
96496         Geometry.prototype.getClass = function getClass () {
96497           return Geometry
96498         };
96499         Geometry.hasNonEmptyElements = function hasNonEmptyElements (geometries) {
96500           for (var i = 0; i < geometries.length; i++) {
96501             if (!geometries[i].isEmpty()) {
96502               return true
96503             }
96504           }
96505           return false
96506         };
96507         Geometry.hasNullElements = function hasNullElements (array) {
96508           for (var i = 0; i < array.length; i++) {
96509             if (array[i] === null) {
96510               return true
96511             }
96512           }
96513           return false
96514         };
96515         staticAccessors$11.serialVersionUID.get = function () { return 8763622679187376702 };
96516         staticAccessors$11.SORTINDEX_POINT.get = function () { return 0 };
96517         staticAccessors$11.SORTINDEX_MULTIPOINT.get = function () { return 1 };
96518         staticAccessors$11.SORTINDEX_LINESTRING.get = function () { return 2 };
96519         staticAccessors$11.SORTINDEX_LINEARRING.get = function () { return 3 };
96520         staticAccessors$11.SORTINDEX_MULTILINESTRING.get = function () { return 4 };
96521         staticAccessors$11.SORTINDEX_POLYGON.get = function () { return 5 };
96522         staticAccessors$11.SORTINDEX_MULTIPOLYGON.get = function () { return 6 };
96523         staticAccessors$11.SORTINDEX_GEOMETRYCOLLECTION.get = function () { return 7 };
96524         staticAccessors$11.geometryChangedFilter.get = function () { return geometryChangedFilter };
96525
96526         Object.defineProperties( Geometry, staticAccessors$11 );
96527
96528         var geometryChangedFilter = function geometryChangedFilter () {};
96529
96530         geometryChangedFilter.interfaces_ = function interfaces_ () {
96531           return [GeometryComponentFilter]
96532         };
96533         geometryChangedFilter.filter = function filter (geom) {
96534           geom.geometryChangedAction();
96535         };
96536
96537         var CoordinateFilter = function CoordinateFilter () {};
96538
96539         CoordinateFilter.prototype.filter = function filter (coord) {};
96540         CoordinateFilter.prototype.interfaces_ = function interfaces_ () {
96541           return []
96542         };
96543         CoordinateFilter.prototype.getClass = function getClass () {
96544           return CoordinateFilter
96545         };
96546
96547         var BoundaryNodeRule = function BoundaryNodeRule () {};
96548
96549         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 } };
96550
96551         BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {};
96552         BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96553           return []
96554         };
96555         BoundaryNodeRule.prototype.getClass = function getClass () {
96556           return BoundaryNodeRule
96557         };
96558         staticAccessors$12.Mod2BoundaryNodeRule.get = function () { return Mod2BoundaryNodeRule };
96559         staticAccessors$12.EndPointBoundaryNodeRule.get = function () { return EndPointBoundaryNodeRule };
96560         staticAccessors$12.MultiValentEndPointBoundaryNodeRule.get = function () { return MultiValentEndPointBoundaryNodeRule };
96561         staticAccessors$12.MonoValentEndPointBoundaryNodeRule.get = function () { return MonoValentEndPointBoundaryNodeRule };
96562         staticAccessors$12.MOD2_BOUNDARY_RULE.get = function () { return new Mod2BoundaryNodeRule() };
96563         staticAccessors$12.ENDPOINT_BOUNDARY_RULE.get = function () { return new EndPointBoundaryNodeRule() };
96564         staticAccessors$12.MULTIVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MultiValentEndPointBoundaryNodeRule() };
96565         staticAccessors$12.MONOVALENT_ENDPOINT_BOUNDARY_RULE.get = function () { return new MonoValentEndPointBoundaryNodeRule() };
96566         staticAccessors$12.OGC_SFS_BOUNDARY_RULE.get = function () { return BoundaryNodeRule.MOD2_BOUNDARY_RULE };
96567
96568         Object.defineProperties( BoundaryNodeRule, staticAccessors$12 );
96569
96570         var Mod2BoundaryNodeRule = function Mod2BoundaryNodeRule () {};
96571
96572         Mod2BoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96573           return boundaryCount % 2 === 1
96574         };
96575         Mod2BoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96576           return [BoundaryNodeRule]
96577         };
96578         Mod2BoundaryNodeRule.prototype.getClass = function getClass () {
96579           return Mod2BoundaryNodeRule
96580         };
96581
96582         var EndPointBoundaryNodeRule = function EndPointBoundaryNodeRule () {};
96583
96584         EndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96585           return boundaryCount > 0
96586         };
96587         EndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96588           return [BoundaryNodeRule]
96589         };
96590         EndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96591           return EndPointBoundaryNodeRule
96592         };
96593
96594         var MultiValentEndPointBoundaryNodeRule = function MultiValentEndPointBoundaryNodeRule () {};
96595
96596         MultiValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96597           return boundaryCount > 1
96598         };
96599         MultiValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96600           return [BoundaryNodeRule]
96601         };
96602         MultiValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96603           return MultiValentEndPointBoundaryNodeRule
96604         };
96605
96606         var MonoValentEndPointBoundaryNodeRule = function MonoValentEndPointBoundaryNodeRule () {};
96607
96608         MonoValentEndPointBoundaryNodeRule.prototype.isInBoundary = function isInBoundary (boundaryCount) {
96609           return boundaryCount === 1
96610         };
96611         MonoValentEndPointBoundaryNodeRule.prototype.interfaces_ = function interfaces_ () {
96612           return [BoundaryNodeRule]
96613         };
96614         MonoValentEndPointBoundaryNodeRule.prototype.getClass = function getClass () {
96615           return MonoValentEndPointBoundaryNodeRule
96616         };
96617
96618         // import Iterator from './Iterator'
96619
96620         /**
96621          * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html
96622          *
96623          * @constructor
96624          * @private
96625          */
96626         var Collection = function Collection () {};
96627
96628         Collection.prototype.add = function add () {};
96629
96630         /**
96631          * Appends all of the elements in the specified collection to the end of this
96632          * list, in the order that they are returned by the specified collection's
96633          * iterator (optional operation).
96634          * @param {javascript.util.Collection} c
96635          * @return {boolean}
96636          */
96637         Collection.prototype.addAll = function addAll () {};
96638
96639         /**
96640          * Returns true if this collection contains no elements.
96641          * @return {boolean}
96642          */
96643         Collection.prototype.isEmpty = function isEmpty () {};
96644
96645         /**
96646          * Returns an iterator over the elements in this collection.
96647          * @return {javascript.util.Iterator}
96648          */
96649         Collection.prototype.iterator = function iterator () {};
96650
96651         /**
96652          * Returns an iterator over the elements in this collection.
96653          * @return {number}
96654          */
96655         Collection.prototype.size = function size () {};
96656
96657         /**
96658          * Returns an array containing all of the elements in this collection.
96659          * @return {Array}
96660          */
96661         Collection.prototype.toArray = function toArray () {};
96662
96663         /**
96664          * Removes a single instance of the specified element from this collection if it
96665          * is present. (optional)
96666          * @param {Object} e
96667          * @return {boolean}
96668          */
96669         Collection.prototype.remove = function remove () {};
96670
96671         /**
96672          * @param {string=} message Optional message
96673          * @extends {Error}
96674          * @constructor
96675          * @private
96676          */
96677         function IndexOutOfBoundsException (message) {
96678           this.message = message || '';
96679         }
96680         IndexOutOfBoundsException.prototype = new Error();
96681
96682         /**
96683          * @type {string}
96684          */
96685         IndexOutOfBoundsException.prototype.name = 'IndexOutOfBoundsException';
96686
96687         /**
96688          * @see http://download.oracle.com/javase/6/docs/api/java/util/Iterator.html
96689          * @constructor
96690          * @private
96691          */
96692         var Iterator$1 = function Iterator () {};
96693
96694         Iterator$1.prototype.hasNext = function hasNext () {};
96695
96696         /**
96697          * Returns the next element in the iteration.
96698          * @return {Object}
96699          */
96700         Iterator$1.prototype.next = function next () {};
96701
96702         /**
96703          * Removes from the underlying collection the last element returned by the
96704          * iterator (optional operation).
96705          */
96706         Iterator$1.prototype.remove = function remove () {};
96707
96708         /**
96709          * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html
96710          *
96711          * @extends {javascript.util.Collection}
96712          * @constructor
96713          * @private
96714          */
96715         var List = (function (Collection$$1) {
96716           function List () {
96717             Collection$$1.apply(this, arguments);
96718           }
96719
96720           if ( Collection$$1 ) { List.__proto__ = Collection$$1; }
96721           List.prototype = Object.create( Collection$$1 && Collection$$1.prototype );
96722           List.prototype.constructor = List;
96723
96724           List.prototype.get = function get () { };
96725
96726           /**
96727            * Replaces the element at the specified position in this list with the
96728            * specified element (optional operation).
96729            * @param {number} index
96730            * @param {Object} e
96731            * @return {Object}
96732            */
96733           List.prototype.set = function set () { };
96734
96735           /**
96736            * Returns true if this collection contains no elements.
96737            * @return {boolean}
96738            */
96739           List.prototype.isEmpty = function isEmpty () { };
96740
96741           return List;
96742         }(Collection));
96743
96744         /**
96745          * @param {string=} message Optional message
96746          * @extends {Error}
96747          * @constructor
96748          * @private
96749          */
96750         function NoSuchElementException (message) {
96751           this.message = message || '';
96752         }
96753         NoSuchElementException.prototype = new Error();
96754
96755         /**
96756          * @type {string}
96757          */
96758         NoSuchElementException.prototype.name = 'NoSuchElementException';
96759
96760         // import OperationNotSupported from './OperationNotSupported'
96761
96762         /**
96763          * @see http://download.oracle.com/javase/6/docs/api/java/util/ArrayList.html
96764          *
96765          * @extends List
96766          * @private
96767          */
96768         var ArrayList = (function (List$$1) {
96769           function ArrayList () {
96770             List$$1.call(this);
96771             this.array_ = [];
96772
96773             if (arguments[0] instanceof Collection) {
96774               this.addAll(arguments[0]);
96775             }
96776           }
96777
96778           if ( List$$1 ) { ArrayList.__proto__ = List$$1; }
96779           ArrayList.prototype = Object.create( List$$1 && List$$1.prototype );
96780           ArrayList.prototype.constructor = ArrayList;
96781
96782           ArrayList.prototype.ensureCapacity = function ensureCapacity () {};
96783           ArrayList.prototype.interfaces_ = function interfaces_ () { return [List$$1, Collection] };
96784
96785           /**
96786            * @override
96787            */
96788           ArrayList.prototype.add = function add (e) {
96789             if (arguments.length === 1) {
96790               this.array_.push(e);
96791             } else {
96792               this.array_.splice(arguments[0], arguments[1]);
96793             }
96794             return true
96795           };
96796
96797           ArrayList.prototype.clear = function clear () {
96798             this.array_ = [];
96799           };
96800
96801           /**
96802            * @override
96803            */
96804           ArrayList.prototype.addAll = function addAll (c) {
96805             var this$1 = this;
96806
96807             for (var i = c.iterator(); i.hasNext();) {
96808               this$1.add(i.next());
96809             }
96810             return true
96811           };
96812
96813           /**
96814            * @override
96815            */
96816           ArrayList.prototype.set = function set (index, element) {
96817             var oldElement = this.array_[index];
96818             this.array_[index] = element;
96819             return oldElement
96820           };
96821
96822           /**
96823            * @override
96824            */
96825           ArrayList.prototype.iterator = function iterator () {
96826             return new Iterator_(this)
96827           };
96828
96829           /**
96830            * @override
96831            */
96832           ArrayList.prototype.get = function get (index) {
96833             if (index < 0 || index >= this.size()) {
96834               throw new IndexOutOfBoundsException()
96835             }
96836
96837             return this.array_[index]
96838           };
96839
96840           /**
96841            * @override
96842            */
96843           ArrayList.prototype.isEmpty = function isEmpty () {
96844             return this.array_.length === 0
96845           };
96846
96847           /**
96848            * @override
96849            */
96850           ArrayList.prototype.size = function size () {
96851             return this.array_.length
96852           };
96853
96854           /**
96855            * @override
96856            */
96857           ArrayList.prototype.toArray = function toArray () {
96858             var this$1 = this;
96859
96860             var array = [];
96861
96862             for (var i = 0, len = this.array_.length; i < len; i++) {
96863               array.push(this$1.array_[i]);
96864             }
96865
96866             return array
96867           };
96868
96869           /**
96870            * @override
96871            */
96872           ArrayList.prototype.remove = function remove (o) {
96873             var this$1 = this;
96874
96875             var found = false;
96876
96877             for (var i = 0, len = this.array_.length; i < len; i++) {
96878               if (this$1.array_[i] === o) {
96879                 this$1.array_.splice(i, 1);
96880                 found = true;
96881                 break
96882               }
96883             }
96884
96885             return found
96886           };
96887
96888           return ArrayList;
96889         }(List));
96890
96891         /**
96892          * @extends {Iterator}
96893          * @param {ArrayList} arrayList
96894          * @constructor
96895          * @private
96896          */
96897         var Iterator_ = (function (Iterator$$1) {
96898           function Iterator_ (arrayList) {
96899             Iterator$$1.call(this);
96900             /**
96901              * @type {ArrayList}
96902              * @private
96903             */
96904             this.arrayList_ = arrayList;
96905             /**
96906              * @type {number}
96907              * @private
96908             */
96909             this.position_ = 0;
96910           }
96911
96912           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
96913           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
96914           Iterator_.prototype.constructor = Iterator_;
96915
96916           /**
96917            * @override
96918            */
96919           Iterator_.prototype.next = function next () {
96920             if (this.position_ === this.arrayList_.size()) {
96921               throw new NoSuchElementException()
96922             }
96923             return this.arrayList_.get(this.position_++)
96924           };
96925
96926           /**
96927            * @override
96928            */
96929           Iterator_.prototype.hasNext = function hasNext () {
96930             if (this.position_ < this.arrayList_.size()) {
96931               return true
96932             } else {
96933               return false
96934             }
96935           };
96936
96937           /**
96938            * TODO: should be in ListIterator
96939            * @override
96940            */
96941           Iterator_.prototype.set = function set (element) {
96942             return this.arrayList_.set(this.position_ - 1, element)
96943           };
96944
96945           /**
96946            * @override
96947            */
96948           Iterator_.prototype.remove = function remove () {
96949             this.arrayList_.remove(this.arrayList_.get(this.position_));
96950           };
96951
96952           return Iterator_;
96953         }(Iterator$1));
96954
96955         var CoordinateList = (function (ArrayList$$1) {
96956           function CoordinateList () {
96957             ArrayList$$1.call(this);
96958             if (arguments.length === 0) ; else if (arguments.length === 1) {
96959               var coord = arguments[0];
96960               this.ensureCapacity(coord.length);
96961               this.add(coord, true);
96962             } else if (arguments.length === 2) {
96963               var coord$1 = arguments[0];
96964               var allowRepeated = arguments[1];
96965               this.ensureCapacity(coord$1.length);
96966               this.add(coord$1, allowRepeated);
96967             }
96968           }
96969
96970           if ( ArrayList$$1 ) { CoordinateList.__proto__ = ArrayList$$1; }
96971           CoordinateList.prototype = Object.create( ArrayList$$1 && ArrayList$$1.prototype );
96972           CoordinateList.prototype.constructor = CoordinateList;
96973
96974           var staticAccessors = { coordArrayType: { configurable: true } };
96975           staticAccessors.coordArrayType.get = function () { return new Array(0).fill(null) };
96976           CoordinateList.prototype.getCoordinate = function getCoordinate (i) {
96977             return this.get(i)
96978           };
96979           CoordinateList.prototype.addAll = function addAll () {
96980             var this$1 = this;
96981
96982             if (arguments.length === 2) {
96983               var coll = arguments[0];
96984               var allowRepeated = arguments[1];
96985               var isChanged = false;
96986               for (var i = coll.iterator(); i.hasNext();) {
96987                 this$1.add(i.next(), allowRepeated);
96988                 isChanged = true;
96989               }
96990               return isChanged
96991             } else { return ArrayList$$1.prototype.addAll.apply(this, arguments) }
96992           };
96993           CoordinateList.prototype.clone = function clone () {
96994             var this$1 = this;
96995
96996             var clone = ArrayList$$1.prototype.clone.call(this);
96997             for (var i = 0; i < this.size(); i++) {
96998               clone.add(i, this$1.get(i).copy());
96999             }
97000             return clone
97001           };
97002           CoordinateList.prototype.toCoordinateArray = function toCoordinateArray () {
97003             return this.toArray(CoordinateList.coordArrayType)
97004           };
97005           CoordinateList.prototype.add = function add () {
97006             var this$1 = this;
97007
97008             if (arguments.length === 1) {
97009               var coord = arguments[0];
97010               ArrayList$$1.prototype.add.call(this, coord);
97011             } else if (arguments.length === 2) {
97012               if (arguments[0] instanceof Array && typeof arguments[1] === 'boolean') {
97013                 var coord$1 = arguments[0];
97014                 var allowRepeated = arguments[1];
97015                 this.add(coord$1, allowRepeated, true);
97016                 return true
97017               } else if (arguments[0] instanceof Coordinate && typeof arguments[1] === 'boolean') {
97018                 var coord$2 = arguments[0];
97019                 var allowRepeated$1 = arguments[1];
97020                 if (!allowRepeated$1) {
97021                   if (this.size() >= 1) {
97022                     var last = this.get(this.size() - 1);
97023                     if (last.equals2D(coord$2)) { return null }
97024                   }
97025                 }
97026                 ArrayList$$1.prototype.add.call(this, coord$2);
97027               } else if (arguments[0] instanceof Object && typeof arguments[1] === 'boolean') {
97028                 var obj = arguments[0];
97029                 var allowRepeated$2 = arguments[1];
97030                 this.add(obj, allowRepeated$2);
97031                 return true
97032               }
97033             } else if (arguments.length === 3) {
97034               if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Array && typeof arguments[1] === 'boolean')) {
97035                 var coord$3 = arguments[0];
97036                 var allowRepeated$3 = arguments[1];
97037                 var direction = arguments[2];
97038                 if (direction) {
97039                   for (var i$1 = 0; i$1 < coord$3.length; i$1++) {
97040                     this$1.add(coord$3[i$1], allowRepeated$3);
97041                   }
97042                 } else {
97043                   for (var i$2 = coord$3.length - 1; i$2 >= 0; i$2--) {
97044                     this$1.add(coord$3[i$2], allowRepeated$3);
97045                   }
97046                 }
97047                 return true
97048               } else if (typeof arguments[2] === 'boolean' && (Number.isInteger(arguments[0]) && arguments[1] instanceof Coordinate)) {
97049                 var i$3 = arguments[0];
97050                 var coord$4 = arguments[1];
97051                 var allowRepeated$4 = arguments[2];
97052                 if (!allowRepeated$4) {
97053                   var size = this.size();
97054                   if (size > 0) {
97055                     if (i$3 > 0) {
97056                       var prev = this.get(i$3 - 1);
97057                       if (prev.equals2D(coord$4)) { return null }
97058                     }
97059                     if (i$3 < size) {
97060                       var next = this.get(i$3);
97061                       if (next.equals2D(coord$4)) { return null }
97062                     }
97063                   }
97064                 }
97065                 ArrayList$$1.prototype.add.call(this, i$3, coord$4);
97066               }
97067             } else if (arguments.length === 4) {
97068               var coord$5 = arguments[0];
97069               var allowRepeated$5 = arguments[1];
97070               var start = arguments[2];
97071               var end = arguments[3];
97072               var inc = 1;
97073               if (start > end) { inc = -1; }
97074               for (var i = start; i !== end; i += inc) {
97075                 this$1.add(coord$5[i], allowRepeated$5);
97076               }
97077               return true
97078             }
97079           };
97080           CoordinateList.prototype.closeRing = function closeRing () {
97081             if (this.size() > 0) { this.add(new Coordinate(this.get(0)), false); }
97082           };
97083           CoordinateList.prototype.interfaces_ = function interfaces_ () {
97084             return []
97085           };
97086           CoordinateList.prototype.getClass = function getClass () {
97087             return CoordinateList
97088           };
97089
97090           Object.defineProperties( CoordinateList, staticAccessors );
97091
97092           return CoordinateList;
97093         }(ArrayList));
97094
97095         var CoordinateArrays = function CoordinateArrays () {};
97096
97097         var staticAccessors$13 = { ForwardComparator: { configurable: true },BidirectionalComparator: { configurable: true },coordArrayType: { configurable: true } };
97098
97099         staticAccessors$13.ForwardComparator.get = function () { return ForwardComparator };
97100         staticAccessors$13.BidirectionalComparator.get = function () { return BidirectionalComparator };
97101         staticAccessors$13.coordArrayType.get = function () { return new Array(0).fill(null) };
97102
97103         CoordinateArrays.prototype.interfaces_ = function interfaces_ () {
97104           return []
97105         };
97106         CoordinateArrays.prototype.getClass = function getClass () {
97107           return CoordinateArrays
97108         };
97109         CoordinateArrays.isRing = function isRing (pts) {
97110           if (pts.length < 4) { return false }
97111           if (!pts[0].equals2D(pts[pts.length - 1])) { return false }
97112           return true
97113         };
97114         CoordinateArrays.ptNotInList = function ptNotInList (testPts, pts) {
97115           for (var i = 0; i < testPts.length; i++) {
97116             var testPt = testPts[i];
97117             if (CoordinateArrays.indexOf(testPt, pts) < 0) { return testPt }
97118           }
97119           return null
97120         };
97121         CoordinateArrays.scroll = function scroll (coordinates, firstCoordinate) {
97122           var i = CoordinateArrays.indexOf(firstCoordinate, coordinates);
97123           if (i < 0) { return null }
97124           var newCoordinates = new Array(coordinates.length).fill(null);
97125           System.arraycopy(coordinates, i, newCoordinates, 0, coordinates.length - i);
97126           System.arraycopy(coordinates, 0, newCoordinates, coordinates.length - i, i);
97127           System.arraycopy(newCoordinates, 0, coordinates, 0, coordinates.length);
97128         };
97129         CoordinateArrays.equals = function equals () {
97130           if (arguments.length === 2) {
97131             var coord1 = arguments[0];
97132             var coord2 = arguments[1];
97133             if (coord1 === coord2) { return true }
97134             if (coord1 === null || coord2 === null) { return false }
97135             if (coord1.length !== coord2.length) { return false }
97136             for (var i = 0; i < coord1.length; i++) {
97137               if (!coord1[i].equals(coord2[i])) { return false }
97138             }
97139             return true
97140           } else if (arguments.length === 3) {
97141             var coord1$1 = arguments[0];
97142             var coord2$1 = arguments[1];
97143             var coordinateComparator = arguments[2];
97144             if (coord1$1 === coord2$1) { return true }
97145             if (coord1$1 === null || coord2$1 === null) { return false }
97146             if (coord1$1.length !== coord2$1.length) { return false }
97147             for (var i$1 = 0; i$1 < coord1$1.length; i$1++) {
97148               if (coordinateComparator.compare(coord1$1[i$1], coord2$1[i$1]) !== 0) { return false }
97149             }
97150             return true
97151           }
97152         };
97153         CoordinateArrays.intersection = function intersection (coordinates, env) {
97154           var coordList = new CoordinateList();
97155           for (var i = 0; i < coordinates.length; i++) {
97156             if (env.intersects(coordinates[i])) { coordList.add(coordinates[i], true); }
97157           }
97158           return coordList.toCoordinateArray()
97159         };
97160         CoordinateArrays.hasRepeatedPoints = function hasRepeatedPoints (coord) {
97161           for (var i = 1; i < coord.length; i++) {
97162             if (coord[i - 1].equals(coord[i])) {
97163               return true
97164             }
97165           }
97166           return false
97167         };
97168         CoordinateArrays.removeRepeatedPoints = function removeRepeatedPoints (coord) {
97169           if (!CoordinateArrays.hasRepeatedPoints(coord)) { return coord }
97170           var coordList = new CoordinateList(coord, false);
97171           return coordList.toCoordinateArray()
97172         };
97173         CoordinateArrays.reverse = function reverse (coord) {
97174           var last = coord.length - 1;
97175           var mid = Math.trunc(last / 2);
97176           for (var i = 0; i <= mid; i++) {
97177             var tmp = coord[i];
97178             coord[i] = coord[last - i];
97179             coord[last - i] = tmp;
97180           }
97181         };
97182         CoordinateArrays.removeNull = function removeNull (coord) {
97183           var nonNull = 0;
97184           for (var i = 0; i < coord.length; i++) {
97185             if (coord[i] !== null) { nonNull++; }
97186           }
97187           var newCoord = new Array(nonNull).fill(null);
97188           if (nonNull === 0) { return newCoord }
97189           var j = 0;
97190           for (var i$1 = 0; i$1 < coord.length; i$1++) {
97191             if (coord[i$1] !== null) { newCoord[j++] = coord[i$1]; }
97192           }
97193           return newCoord
97194         };
97195         CoordinateArrays.copyDeep = function copyDeep () {
97196           if (arguments.length === 1) {
97197             var coordinates = arguments[0];
97198             var copy = new Array(coordinates.length).fill(null);
97199             for (var i = 0; i < coordinates.length; i++) {
97200               copy[i] = new Coordinate(coordinates[i]);
97201             }
97202             return copy
97203           } else if (arguments.length === 5) {
97204             var src = arguments[0];
97205             var srcStart = arguments[1];
97206             var dest = arguments[2];
97207             var destStart = arguments[3];
97208             var length = arguments[4];
97209             for (var i$1 = 0; i$1 < length; i$1++) {
97210               dest[destStart + i$1] = new Coordinate(src[srcStart + i$1]);
97211             }
97212           }
97213         };
97214         CoordinateArrays.isEqualReversed = function isEqualReversed (pts1, pts2) {
97215           for (var i = 0; i < pts1.length; i++) {
97216             var p1 = pts1[i];
97217             var p2 = pts2[pts1.length - i - 1];
97218             if (p1.compareTo(p2) !== 0) { return false }
97219           }
97220           return true
97221         };
97222         CoordinateArrays.envelope = function envelope (coordinates) {
97223           var env = new Envelope();
97224           for (var i = 0; i < coordinates.length; i++) {
97225             env.expandToInclude(coordinates[i]);
97226           }
97227           return env
97228         };
97229         CoordinateArrays.toCoordinateArray = function toCoordinateArray (coordList) {
97230           return coordList.toArray(CoordinateArrays.coordArrayType)
97231         };
97232         CoordinateArrays.atLeastNCoordinatesOrNothing = function atLeastNCoordinatesOrNothing (n, c) {
97233           return c.length >= n ? c : []
97234         };
97235         CoordinateArrays.indexOf = function indexOf (coordinate, coordinates) {
97236           for (var i = 0; i < coordinates.length; i++) {
97237             if (coordinate.equals(coordinates[i])) {
97238               return i
97239             }
97240           }
97241           return -1
97242         };
97243         CoordinateArrays.increasingDirection = function increasingDirection (pts) {
97244           for (var i = 0; i < Math.trunc(pts.length / 2); i++) {
97245             var j = pts.length - 1 - i;
97246             var comp = pts[i].compareTo(pts[j]);
97247             if (comp !== 0) { return comp }
97248           }
97249           return 1
97250         };
97251         CoordinateArrays.compare = function compare (pts1, pts2) {
97252           var i = 0;
97253           while (i < pts1.length && i < pts2.length) {
97254             var compare = pts1[i].compareTo(pts2[i]);
97255             if (compare !== 0) { return compare }
97256             i++;
97257           }
97258           if (i < pts2.length) { return -1 }
97259           if (i < pts1.length) { return 1 }
97260           return 0
97261         };
97262         CoordinateArrays.minCoordinate = function minCoordinate (coordinates) {
97263           var minCoord = null;
97264           for (var i = 0; i < coordinates.length; i++) {
97265             if (minCoord === null || minCoord.compareTo(coordinates[i]) > 0) {
97266               minCoord = coordinates[i];
97267             }
97268           }
97269           return minCoord
97270         };
97271         CoordinateArrays.extract = function extract (pts, start, end) {
97272           start = MathUtil.clamp(start, 0, pts.length);
97273           end = MathUtil.clamp(end, -1, pts.length);
97274           var npts = end - start + 1;
97275           if (end < 0) { npts = 0; }
97276           if (start >= pts.length) { npts = 0; }
97277           if (end < start) { npts = 0; }
97278           var extractPts = new Array(npts).fill(null);
97279           if (npts === 0) { return extractPts }
97280           var iPts = 0;
97281           for (var i = start; i <= end; i++) {
97282             extractPts[iPts++] = pts[i];
97283           }
97284           return extractPts
97285         };
97286
97287         Object.defineProperties( CoordinateArrays, staticAccessors$13 );
97288
97289         var ForwardComparator = function ForwardComparator () {};
97290
97291         ForwardComparator.prototype.compare = function compare (o1, o2) {
97292           var pts1 = o1;
97293           var pts2 = o2;
97294           return CoordinateArrays.compare(pts1, pts2)
97295         };
97296         ForwardComparator.prototype.interfaces_ = function interfaces_ () {
97297           return [Comparator]
97298         };
97299         ForwardComparator.prototype.getClass = function getClass () {
97300           return ForwardComparator
97301         };
97302
97303         var BidirectionalComparator = function BidirectionalComparator () {};
97304
97305         BidirectionalComparator.prototype.compare = function compare (o1, o2) {
97306           var pts1 = o1;
97307           var pts2 = o2;
97308           if (pts1.length < pts2.length) { return -1 }
97309           if (pts1.length > pts2.length) { return 1 }
97310           if (pts1.length === 0) { return 0 }
97311           var forwardComp = CoordinateArrays.compare(pts1, pts2);
97312           var isEqualRev = CoordinateArrays.isEqualReversed(pts1, pts2);
97313           if (isEqualRev) { return 0 }
97314           return forwardComp
97315         };
97316         BidirectionalComparator.prototype.OLDcompare = function OLDcompare (o1, o2) {
97317           var pts1 = o1;
97318           var pts2 = o2;
97319           if (pts1.length < pts2.length) { return -1 }
97320           if (pts1.length > pts2.length) { return 1 }
97321           if (pts1.length === 0) { return 0 }
97322           var dir1 = CoordinateArrays.increasingDirection(pts1);
97323           var dir2 = CoordinateArrays.increasingDirection(pts2);
97324           var i1 = dir1 > 0 ? 0 : pts1.length - 1;
97325           var i2 = dir2 > 0 ? 0 : pts1.length - 1;
97326           for (var i = 0; i < pts1.length; i++) {
97327             var comparePt = pts1[i1].compareTo(pts2[i2]);
97328             if (comparePt !== 0) { return comparePt }
97329             i1 += dir1;
97330             i2 += dir2;
97331           }
97332           return 0
97333         };
97334         BidirectionalComparator.prototype.interfaces_ = function interfaces_ () {
97335           return [Comparator]
97336         };
97337         BidirectionalComparator.prototype.getClass = function getClass () {
97338           return BidirectionalComparator
97339         };
97340
97341         /**
97342          * @see http://download.oracle.com/javase/6/docs/api/java/util/Map.html
97343          *
97344          * @constructor
97345          * @private
97346          */
97347         var Map$1$1 = function Map () {};
97348
97349         Map$1$1.prototype.get = function get () {};
97350         /**
97351          * Associates the specified value with the specified key in this map (optional
97352          * operation).
97353          * @param {Object} key
97354          * @param {Object} value
97355          * @return {Object}
97356          */
97357         Map$1$1.prototype.put = function put () {};
97358
97359         /**
97360          * Returns the number of key-value mappings in this map.
97361          * @return {number}
97362          */
97363         Map$1$1.prototype.size = function size () {};
97364
97365         /**
97366          * Returns a Collection view of the values contained in this map.
97367          * @return {javascript.util.Collection}
97368          */
97369         Map$1$1.prototype.values = function values () {};
97370
97371         /**
97372          * Returns a {@link Set} view of the mappings contained in this map.
97373          * The set is backed by the map, so changes to the map are
97374          * reflected in the set, and vice-versa.If the map is modified
97375          * while an iteration over the set is in progress (except through
97376          * the iterator's own <tt>remove</tt> operation, or through the
97377          * <tt>setValue</tt> operation on a map entry returned by the
97378          * iterator) the results of the iteration are undefined.The set
97379          * supports element removal, which removes the corresponding
97380          * mapping from the map, via the <tt>Iterator.remove</tt>,
97381          * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt> and
97382          * <tt>clear</tt> operations.It does not support the
97383          * <tt>add</tt> or <tt>addAll</tt> operations.
97384          *
97385          * @return {Set} a set view of the mappings contained in this map
97386          */
97387         Map$1$1.prototype.entrySet = function entrySet () {};
97388
97389         /**
97390          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedMap.html
97391          *
97392          * @extends {Map}
97393          * @constructor
97394          * @private
97395          */
97396         var SortedMap = (function (Map) {
97397                 function SortedMap () {
97398                         Map.apply(this, arguments);
97399                 }if ( Map ) { SortedMap.__proto__ = Map; }
97400                 SortedMap.prototype = Object.create( Map && Map.prototype );
97401                 SortedMap.prototype.constructor = SortedMap;
97402
97403                 
97404
97405                 return SortedMap;
97406         }(Map$1$1));
97407
97408         /**
97409          * @param {string=} message Optional message
97410          * @extends {Error}
97411          * @constructor
97412          * @private
97413          */
97414         function OperationNotSupported (message) {
97415           this.message = message || '';
97416         }
97417         OperationNotSupported.prototype = new Error();
97418
97419         /**
97420          * @type {string}
97421          */
97422         OperationNotSupported.prototype.name = 'OperationNotSupported';
97423
97424         /**
97425          * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html
97426          *
97427          * @extends {Collection}
97428          * @constructor
97429          * @private
97430          */
97431         function Set$2() {}
97432         Set$2.prototype = new Collection();
97433
97434
97435         /**
97436          * Returns true if this set contains the specified element. More formally,
97437          * returns true if and only if this set contains an element e such that (o==null ?
97438          * e==null : o.equals(e)).
97439          * @param {Object} e
97440          * @return {boolean}
97441          */
97442         Set$2.prototype.contains = function() {};
97443
97444         /**
97445          * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html
97446          *
97447          * @extends {javascript.util.Set}
97448          * @constructor
97449          * @private
97450          */
97451         var HashSet = (function (Set$$1) {
97452           function HashSet () {
97453             Set$$1.call(this);
97454             this.array_ = [];
97455
97456             if (arguments[0] instanceof Collection) {
97457               this.addAll(arguments[0]);
97458             }
97459           }
97460
97461           if ( Set$$1 ) { HashSet.__proto__ = Set$$1; }
97462           HashSet.prototype = Object.create( Set$$1 && Set$$1.prototype );
97463           HashSet.prototype.constructor = HashSet;
97464
97465           /**
97466            * @override
97467            */
97468           HashSet.prototype.contains = function contains (o) {
97469             var this$1 = this;
97470
97471             for (var i = 0, len = this.array_.length; i < len; i++) {
97472               var e = this$1.array_[i];
97473               if (e === o) {
97474                 return true
97475               }
97476             }
97477             return false
97478           };
97479
97480           /**
97481            * @override
97482            */
97483           HashSet.prototype.add = function add (o) {
97484             if (this.contains(o)) {
97485               return false
97486             }
97487
97488             this.array_.push(o);
97489
97490             return true
97491           };
97492
97493           /**
97494            * @override
97495            */
97496           HashSet.prototype.addAll = function addAll (c) {
97497             var this$1 = this;
97498
97499             for (var i = c.iterator(); i.hasNext();) {
97500               this$1.add(i.next());
97501             }
97502             return true
97503           };
97504
97505           /**
97506            * @override
97507            */
97508           HashSet.prototype.remove = function remove (o) {
97509             // throw new javascript.util.OperationNotSupported()
97510             throw new Error()
97511           };
97512
97513           /**
97514            * @override
97515            */
97516           HashSet.prototype.size = function size () {
97517             return this.array_.length
97518           };
97519
97520           /**
97521            * @override
97522            */
97523           HashSet.prototype.isEmpty = function isEmpty () {
97524             return this.array_.length === 0
97525           };
97526
97527           /**
97528            * @override
97529            */
97530           HashSet.prototype.toArray = function toArray () {
97531             var this$1 = this;
97532
97533             var array = [];
97534
97535             for (var i = 0, len = this.array_.length; i < len; i++) {
97536               array.push(this$1.array_[i]);
97537             }
97538
97539             return array
97540           };
97541
97542           /**
97543            * @override
97544            */
97545           HashSet.prototype.iterator = function iterator () {
97546             return new Iterator_$1(this)
97547           };
97548
97549           return HashSet;
97550         }(Set$2));
97551
97552         /**
97553            * @extends {Iterator}
97554            * @param {HashSet} hashSet
97555            * @constructor
97556            * @private
97557            */
97558         var Iterator_$1 = (function (Iterator$$1) {
97559           function Iterator_ (hashSet) {
97560             Iterator$$1.call(this);
97561             /**
97562              * @type {HashSet}
97563              * @private
97564              */
97565             this.hashSet_ = hashSet;
97566             /**
97567              * @type {number}
97568              * @private
97569              */
97570             this.position_ = 0;
97571           }
97572
97573           if ( Iterator$$1 ) { Iterator_.__proto__ = Iterator$$1; }
97574           Iterator_.prototype = Object.create( Iterator$$1 && Iterator$$1.prototype );
97575           Iterator_.prototype.constructor = Iterator_;
97576
97577           /**
97578            * @override
97579            */
97580           Iterator_.prototype.next = function next () {
97581             if (this.position_ === this.hashSet_.size()) {
97582               throw new NoSuchElementException()
97583             }
97584             return this.hashSet_.array_[this.position_++]
97585           };
97586
97587           /**
97588            * @override
97589            */
97590           Iterator_.prototype.hasNext = function hasNext () {
97591             if (this.position_ < this.hashSet_.size()) {
97592               return true
97593             } else {
97594               return false
97595             }
97596           };
97597
97598           /**
97599            * @override
97600            */
97601           Iterator_.prototype.remove = function remove () {
97602             throw new OperationNotSupported()
97603           };
97604
97605           return Iterator_;
97606         }(Iterator$1));
97607
97608         var BLACK = 0;
97609         var RED = 1;
97610         function colorOf (p) { return (p === null ? BLACK : p.color) }
97611         function parentOf (p) { return (p === null ? null : p.parent) }
97612         function setColor (p, c) { if (p !== null) { p.color = c; } }
97613         function leftOf (p) { return (p === null ? null : p.left) }
97614         function rightOf (p) { return (p === null ? null : p.right) }
97615
97616         /**
97617          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeMap.html
97618          *
97619          * @extends {SortedMap}
97620          * @constructor
97621          * @private
97622          */
97623         function TreeMap () {
97624           /**
97625            * @type {Object}
97626            * @private
97627            */
97628           this.root_ = null;
97629           /**
97630            * @type {number}
97631            * @private
97632           */
97633           this.size_ = 0;
97634         }
97635         TreeMap.prototype = new SortedMap();
97636
97637         /**
97638          * @override
97639          */
97640         TreeMap.prototype.get = function (key) {
97641           var p = this.root_;
97642           while (p !== null) {
97643             var cmp = key['compareTo'](p.key);
97644             if (cmp < 0) { p = p.left; }
97645             else if (cmp > 0) { p = p.right; }
97646             else { return p.value }
97647           }
97648           return null
97649         };
97650
97651         /**
97652          * @override
97653          */
97654         TreeMap.prototype.put = function (key, value) {
97655           if (this.root_ === null) {
97656             this.root_ = {
97657               key: key,
97658               value: value,
97659               left: null,
97660               right: null,
97661               parent: null,
97662               color: BLACK,
97663               getValue: function getValue () { return this.value },
97664               getKey: function getKey () { return this.key }
97665             };
97666             this.size_ = 1;
97667             return null
97668           }
97669           var t = this.root_;
97670           var parent;
97671           var cmp;
97672           do {
97673             parent = t;
97674             cmp = key['compareTo'](t.key);
97675             if (cmp < 0) {
97676               t = t.left;
97677             } else if (cmp > 0) {
97678               t = t.right;
97679             } else {
97680               var oldValue = t.value;
97681               t.value = value;
97682               return oldValue
97683             }
97684           } while (t !== null)
97685           var e = {
97686             key: key,
97687             left: null,
97688             right: null,
97689             value: value,
97690             parent: parent,
97691             color: BLACK,
97692             getValue: function getValue () { return this.value },
97693             getKey: function getKey () { return this.key }
97694           };
97695           if (cmp < 0) {
97696             parent.left = e;
97697           } else {
97698             parent.right = e;
97699           }
97700           this.fixAfterInsertion(e);
97701           this.size_++;
97702           return null
97703         };
97704
97705         /**
97706          * @param {Object} x
97707          */
97708         TreeMap.prototype.fixAfterInsertion = function (x) {
97709           var this$1 = this;
97710
97711           x.color = RED;
97712           while (x != null && x !== this.root_ && x.parent.color === RED) {
97713             if (parentOf(x) === leftOf(parentOf(parentOf(x)))) {
97714               var y = rightOf(parentOf(parentOf(x)));
97715               if (colorOf(y) === RED) {
97716                 setColor(parentOf(x), BLACK);
97717                 setColor(y, BLACK);
97718                 setColor(parentOf(parentOf(x)), RED);
97719                 x = parentOf(parentOf(x));
97720               } else {
97721                 if (x === rightOf(parentOf(x))) {
97722                   x = parentOf(x);
97723                   this$1.rotateLeft(x);
97724                 }
97725                 setColor(parentOf(x), BLACK);
97726                 setColor(parentOf(parentOf(x)), RED);
97727                 this$1.rotateRight(parentOf(parentOf(x)));
97728               }
97729             } else {
97730               var y$1 = leftOf(parentOf(parentOf(x)));
97731               if (colorOf(y$1) === RED) {
97732                 setColor(parentOf(x), BLACK);
97733                 setColor(y$1, BLACK);
97734                 setColor(parentOf(parentOf(x)), RED);
97735                 x = parentOf(parentOf(x));
97736               } else {
97737                 if (x === leftOf(parentOf(x))) {
97738                   x = parentOf(x);
97739                   this$1.rotateRight(x);
97740                 }
97741                 setColor(parentOf(x), BLACK);
97742                 setColor(parentOf(parentOf(x)), RED);
97743                 this$1.rotateLeft(parentOf(parentOf(x)));
97744               }
97745             }
97746           }
97747           this.root_.color = BLACK;
97748         };
97749
97750         /**
97751          * @override
97752          */
97753         TreeMap.prototype.values = function () {
97754           var arrayList = new ArrayList();
97755           var p = this.getFirstEntry();
97756           if (p !== null) {
97757             arrayList.add(p.value);
97758             while ((p = TreeMap.successor(p)) !== null) {
97759               arrayList.add(p.value);
97760             }
97761           }
97762           return arrayList
97763         };
97764
97765         /**
97766          * @override
97767          */
97768         TreeMap.prototype.entrySet = function () {
97769           var hashSet = new HashSet();
97770           var p = this.getFirstEntry();
97771           if (p !== null) {
97772             hashSet.add(p);
97773             while ((p = TreeMap.successor(p)) !== null) {
97774               hashSet.add(p);
97775             }
97776           }
97777           return hashSet
97778         };
97779
97780         /**
97781          * @param {Object} p
97782          */
97783         TreeMap.prototype.rotateLeft = function (p) {
97784           if (p != null) {
97785             var r = p.right;
97786             p.right = r.left;
97787             if (r.left != null) { r.left.parent = p; }
97788             r.parent = p.parent;
97789             if (p.parent === null) { this.root_ = r; } else if (p.parent.left === p) { p.parent.left = r; } else { p.parent.right = r; }
97790             r.left = p;
97791             p.parent = r;
97792           }
97793         };
97794
97795         /**
97796          * @param {Object} p
97797          */
97798         TreeMap.prototype.rotateRight = function (p) {
97799           if (p != null) {
97800             var l = p.left;
97801             p.left = l.right;
97802             if (l.right != null) { l.right.parent = p; }
97803             l.parent = p.parent;
97804             if (p.parent === null) { this.root_ = l; } else if (p.parent.right === p) { p.parent.right = l; } else { p.parent.left = l; }
97805             l.right = p;
97806             p.parent = l;
97807           }
97808         };
97809
97810         /**
97811          * @return {Object}
97812          */
97813         TreeMap.prototype.getFirstEntry = function () {
97814           var p = this.root_;
97815           if (p != null) {
97816             while (p.left != null) {
97817               p = p.left;
97818             }
97819           }
97820           return p
97821         };
97822
97823         /**
97824          * @param {Object} t
97825          * @return {Object}
97826          * @private
97827          */
97828         TreeMap.successor = function (t) {
97829           if (t === null) { return null } else if (t.right !== null) {
97830             var p = t.right;
97831             while (p.left !== null) {
97832               p = p.left;
97833             }
97834             return p
97835           } else {
97836             var p$1 = t.parent;
97837             var ch = t;
97838             while (p$1 !== null && ch === p$1.right) {
97839               ch = p$1;
97840               p$1 = p$1.parent;
97841             }
97842             return p$1
97843           }
97844         };
97845
97846         /**
97847          * @override
97848          */
97849         TreeMap.prototype.size = function () {
97850           return this.size_
97851         };
97852
97853         var Lineal = function Lineal () {};
97854
97855         Lineal.prototype.interfaces_ = function interfaces_ () {
97856           return []
97857         };
97858         Lineal.prototype.getClass = function getClass () {
97859           return Lineal
97860         };
97861
97862         /**
97863          * @see http://download.oracle.com/javase/6/docs/api/java/util/SortedSet.html
97864          *
97865          * @extends {Set}
97866          * @constructor
97867          * @private
97868          */
97869         function SortedSet () {}
97870         SortedSet.prototype = new Set$2();
97871
97872         // import Iterator from './Iterator'
97873         /**
97874          * @see http://download.oracle.com/javase/6/docs/api/java/util/TreeSet.html
97875          *
97876          * @extends {SortedSet}
97877          * @constructor
97878          * @private
97879          */
97880         function TreeSet () {
97881           /**
97882            * @type {Array}
97883            * @private
97884           */
97885           this.array_ = [];
97886
97887           if (arguments[0] instanceof Collection) {
97888             this.addAll(arguments[0]);
97889           }
97890         }
97891         TreeSet.prototype = new SortedSet();
97892
97893         /**
97894          * @override
97895          */
97896         TreeSet.prototype.contains = function (o) {
97897           var this$1 = this;
97898
97899           for (var i = 0, len = this.array_.length; i < len; i++) {
97900             var e = this$1.array_[i];
97901             if (e['compareTo'](o) === 0) {
97902               return true
97903             }
97904           }
97905           return false
97906         };
97907
97908         /**
97909          * @override
97910          */
97911         TreeSet.prototype.add = function (o) {
97912           var this$1 = this;
97913
97914           if (this.contains(o)) {
97915             return false
97916           }
97917
97918           for (var i = 0, len = this.array_.length; i < len; i++) {
97919             var e = this$1.array_[i];
97920             if (e['compareTo'](o) === 1) {
97921               this$1.array_.splice(i, 0, o);
97922               return true
97923             }
97924           }
97925
97926           this.array_.push(o);
97927
97928           return true
97929         };
97930
97931         /**
97932          * @override
97933          */
97934         TreeSet.prototype.addAll = function (c) {
97935           var this$1 = this;
97936
97937           for (var i = c.iterator(); i.hasNext();) {
97938             this$1.add(i.next());
97939           }
97940           return true
97941         };
97942
97943         /**
97944          * @override
97945          */
97946         TreeSet.prototype.remove = function (e) {
97947           throw new OperationNotSupported()
97948         };
97949
97950         /**
97951          * @override
97952          */
97953         TreeSet.prototype.size = function () {
97954           return this.array_.length
97955         };
97956
97957         /**
97958          * @override
97959          */
97960         TreeSet.prototype.isEmpty = function () {
97961           return this.array_.length === 0
97962         };
97963
97964         /**
97965          * @override
97966          */
97967         TreeSet.prototype.toArray = function () {
97968           var this$1 = this;
97969
97970           var array = [];
97971
97972           for (var i = 0, len = this.array_.length; i < len; i++) {
97973             array.push(this$1.array_[i]);
97974           }
97975
97976           return array
97977         };
97978
97979         /**
97980          * @override
97981          */
97982         TreeSet.prototype.iterator = function () {
97983           return new Iterator_$2(this)
97984         };
97985
97986         /**
97987          * @extends {javascript.util.Iterator}
97988          * @param {javascript.util.TreeSet} treeSet
97989          * @constructor
97990          * @private
97991          */
97992         var Iterator_$2 = function (treeSet) {
97993           /**
97994            * @type {javascript.util.TreeSet}
97995            * @private
97996            */
97997           this.treeSet_ = treeSet;
97998           /**
97999            * @type {number}
98000            * @private
98001            */
98002           this.position_ = 0;
98003         };
98004
98005         /**
98006          * @override
98007          */
98008         Iterator_$2.prototype.next = function () {
98009           if (this.position_ === this.treeSet_.size()) {
98010             throw new NoSuchElementException()
98011           }
98012           return this.treeSet_.array_[this.position_++]
98013         };
98014
98015         /**
98016          * @override
98017          */
98018         Iterator_$2.prototype.hasNext = function () {
98019           if (this.position_ < this.treeSet_.size()) {
98020             return true
98021           } else {
98022             return false
98023           }
98024         };
98025
98026         /**
98027          * @override
98028          */
98029         Iterator_$2.prototype.remove = function () {
98030           throw new OperationNotSupported()
98031         };
98032
98033         /**
98034          * @see http://download.oracle.com/javase/6/docs/api/java/util/Arrays.html
98035          *
98036          * @constructor
98037          * @private
98038          */
98039         var Arrays = function Arrays () {};
98040
98041         Arrays.sort = function sort () {
98042           var a = arguments[0];
98043           var i;
98044           var t;
98045           var comparator;
98046           var compare;
98047           if (arguments.length === 1) {
98048             compare = function (a, b) {
98049               return a.compareTo(b)
98050             };
98051             a.sort(compare);
98052           } else if (arguments.length === 2) {
98053             comparator = arguments[1];
98054             compare = function (a, b) {
98055               return comparator['compare'](a, b)
98056             };
98057             a.sort(compare);
98058           } else if (arguments.length === 3) {
98059             t = a.slice(arguments[1], arguments[2]);
98060             t.sort();
98061             var r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
98062             a.splice(0, a.length);
98063             for (i = 0; i < r.length; i++) {
98064               a.push(r[i]);
98065             }
98066           } else if (arguments.length === 4) {
98067             t = a.slice(arguments[1], arguments[2]);
98068             comparator = arguments[3];
98069             compare = function (a, b) {
98070               return comparator['compare'](a, b)
98071             };
98072             t.sort(compare);
98073             r = a.slice(0, arguments[1]).concat(t, a.slice(arguments[2], a.length));
98074             a.splice(0, a.length);
98075             for (i = 0; i < r.length; i++) {
98076               a.push(r[i]);
98077             }
98078           }
98079         };
98080         /**
98081          * @param {Array} array
98082          * @return {ArrayList}
98083          */
98084         Arrays.asList = function asList (array) {
98085           var arrayList = new ArrayList();
98086           for (var i = 0, len = array.length; i < len; i++) {
98087             arrayList.add(array[i]);
98088           }
98089           return arrayList
98090         };
98091
98092         var Dimension = function Dimension () {};
98093
98094         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 } };
98095
98096         staticAccessors$14.P.get = function () { return 0 };
98097         staticAccessors$14.L.get = function () { return 1 };
98098         staticAccessors$14.A.get = function () { return 2 };
98099         staticAccessors$14.FALSE.get = function () { return -1 };
98100         staticAccessors$14.TRUE.get = function () { return -2 };
98101         staticAccessors$14.DONTCARE.get = function () { return -3 };
98102         staticAccessors$14.SYM_FALSE.get = function () { return 'F' };
98103         staticAccessors$14.SYM_TRUE.get = function () { return 'T' };
98104         staticAccessors$14.SYM_DONTCARE.get = function () { return '*' };
98105         staticAccessors$14.SYM_P.get = function () { return '0' };
98106         staticAccessors$14.SYM_L.get = function () { return '1' };
98107         staticAccessors$14.SYM_A.get = function () { return '2' };
98108
98109         Dimension.prototype.interfaces_ = function interfaces_ () {
98110           return []
98111         };
98112         Dimension.prototype.getClass = function getClass () {
98113           return Dimension
98114         };
98115         Dimension.toDimensionSymbol = function toDimensionSymbol (dimensionValue) {
98116           switch (dimensionValue) {
98117             case Dimension.FALSE:
98118               return Dimension.SYM_FALSE
98119             case Dimension.TRUE:
98120               return Dimension.SYM_TRUE
98121             case Dimension.DONTCARE:
98122               return Dimension.SYM_DONTCARE
98123             case Dimension.P:
98124               return Dimension.SYM_P
98125             case Dimension.L:
98126               return Dimension.SYM_L
98127             case Dimension.A:
98128               return Dimension.SYM_A
98129           }
98130           throw new IllegalArgumentException('Unknown dimension value: ' + dimensionValue)
98131         };
98132         Dimension.toDimensionValue = function toDimensionValue (dimensionSymbol) {
98133           switch (Character.toUpperCase(dimensionSymbol)) {
98134             case Dimension.SYM_FALSE:
98135               return Dimension.FALSE
98136             case Dimension.SYM_TRUE:
98137               return Dimension.TRUE
98138             case Dimension.SYM_DONTCARE:
98139               return Dimension.DONTCARE
98140             case Dimension.SYM_P:
98141               return Dimension.P
98142             case Dimension.SYM_L:
98143               return Dimension.L
98144             case Dimension.SYM_A:
98145               return Dimension.A
98146           }
98147           throw new IllegalArgumentException('Unknown dimension symbol: ' + dimensionSymbol)
98148         };
98149
98150         Object.defineProperties( Dimension, staticAccessors$14 );
98151
98152         var GeometryFilter = function GeometryFilter () {};
98153
98154         GeometryFilter.prototype.filter = function filter (geom) {};
98155         GeometryFilter.prototype.interfaces_ = function interfaces_ () {
98156           return []
98157         };
98158         GeometryFilter.prototype.getClass = function getClass () {
98159           return GeometryFilter
98160         };
98161
98162         var CoordinateSequenceFilter = function CoordinateSequenceFilter () {};
98163
98164         CoordinateSequenceFilter.prototype.filter = function filter (seq, i) {};
98165         CoordinateSequenceFilter.prototype.isDone = function isDone () {};
98166         CoordinateSequenceFilter.prototype.isGeometryChanged = function isGeometryChanged () {};
98167         CoordinateSequenceFilter.prototype.interfaces_ = function interfaces_ () {
98168           return []
98169         };
98170         CoordinateSequenceFilter.prototype.getClass = function getClass () {
98171           return CoordinateSequenceFilter
98172         };
98173
98174         var GeometryCollection = (function (Geometry$$1) {
98175           function GeometryCollection (geometries, factory) {
98176             Geometry$$1.call(this, factory);
98177             this._geometries = geometries || [];
98178
98179             if (Geometry$$1.hasNullElements(this._geometries)) {
98180               throw new IllegalArgumentException('geometries must not contain null elements')
98181             }
98182           }
98183
98184           if ( Geometry$$1 ) { GeometryCollection.__proto__ = Geometry$$1; }
98185           GeometryCollection.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98186           GeometryCollection.prototype.constructor = GeometryCollection;
98187
98188           var staticAccessors = { serialVersionUID: { configurable: true } };
98189           GeometryCollection.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98190             var this$1 = this;
98191
98192             var envelope = new Envelope();
98193             for (var i = 0; i < this._geometries.length; i++) {
98194               envelope.expandToInclude(this$1._geometries[i].getEnvelopeInternal());
98195             }
98196             return envelope
98197           };
98198           GeometryCollection.prototype.getGeometryN = function getGeometryN (n) {
98199             return this._geometries[n]
98200           };
98201           GeometryCollection.prototype.getSortIndex = function getSortIndex () {
98202             return Geometry$$1.SORTINDEX_GEOMETRYCOLLECTION
98203           };
98204           GeometryCollection.prototype.getCoordinates = function getCoordinates () {
98205             var this$1 = this;
98206
98207             var coordinates = new Array(this.getNumPoints()).fill(null);
98208             var k = -1;
98209             for (var i = 0; i < this._geometries.length; i++) {
98210               var childCoordinates = this$1._geometries[i].getCoordinates();
98211               for (var j = 0; j < childCoordinates.length; j++) {
98212                 k++;
98213                 coordinates[k] = childCoordinates[j];
98214               }
98215             }
98216             return coordinates
98217           };
98218           GeometryCollection.prototype.getArea = function getArea () {
98219             var this$1 = this;
98220
98221             var area = 0.0;
98222             for (var i = 0; i < this._geometries.length; i++) {
98223               area += this$1._geometries[i].getArea();
98224             }
98225             return area
98226           };
98227           GeometryCollection.prototype.equalsExact = function equalsExact () {
98228             var this$1 = this;
98229
98230             if (arguments.length === 2) {
98231               var other = arguments[0];
98232               var tolerance = arguments[1];
98233               if (!this.isEquivalentClass(other)) {
98234                 return false
98235               }
98236               var otherCollection = other;
98237               if (this._geometries.length !== otherCollection._geometries.length) {
98238                 return false
98239               }
98240               for (var i = 0; i < this._geometries.length; i++) {
98241                 if (!this$1._geometries[i].equalsExact(otherCollection._geometries[i], tolerance)) {
98242                   return false
98243                 }
98244               }
98245               return true
98246             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98247           };
98248           GeometryCollection.prototype.normalize = function normalize () {
98249             var this$1 = this;
98250
98251             for (var i = 0; i < this._geometries.length; i++) {
98252               this$1._geometries[i].normalize();
98253             }
98254             Arrays.sort(this._geometries);
98255           };
98256           GeometryCollection.prototype.getCoordinate = function getCoordinate () {
98257             if (this.isEmpty()) { return null }
98258             return this._geometries[0].getCoordinate()
98259           };
98260           GeometryCollection.prototype.getBoundaryDimension = function getBoundaryDimension () {
98261             var this$1 = this;
98262
98263             var dimension = Dimension.FALSE;
98264             for (var i = 0; i < this._geometries.length; i++) {
98265               dimension = Math.max(dimension, this$1._geometries[i].getBoundaryDimension());
98266             }
98267             return dimension
98268           };
98269           GeometryCollection.prototype.getDimension = function getDimension () {
98270             var this$1 = this;
98271
98272             var dimension = Dimension.FALSE;
98273             for (var i = 0; i < this._geometries.length; i++) {
98274               dimension = Math.max(dimension, this$1._geometries[i].getDimension());
98275             }
98276             return dimension
98277           };
98278           GeometryCollection.prototype.getLength = function getLength () {
98279             var this$1 = this;
98280
98281             var sum = 0.0;
98282             for (var i = 0; i < this._geometries.length; i++) {
98283               sum += this$1._geometries[i].getLength();
98284             }
98285             return sum
98286           };
98287           GeometryCollection.prototype.getNumPoints = function getNumPoints () {
98288             var this$1 = this;
98289
98290             var numPoints = 0;
98291             for (var i = 0; i < this._geometries.length; i++) {
98292               numPoints += this$1._geometries[i].getNumPoints();
98293             }
98294             return numPoints
98295           };
98296           GeometryCollection.prototype.getNumGeometries = function getNumGeometries () {
98297             return this._geometries.length
98298           };
98299           GeometryCollection.prototype.reverse = function reverse () {
98300             var this$1 = this;
98301
98302             var n = this._geometries.length;
98303             var revGeoms = new Array(n).fill(null);
98304             for (var i = 0; i < this._geometries.length; i++) {
98305               revGeoms[i] = this$1._geometries[i].reverse();
98306             }
98307             return this.getFactory().createGeometryCollection(revGeoms)
98308           };
98309           GeometryCollection.prototype.compareToSameClass = function compareToSameClass () {
98310             var this$1 = this;
98311
98312             if (arguments.length === 1) {
98313               var o = arguments[0];
98314               var theseElements = new TreeSet(Arrays.asList(this._geometries));
98315               var otherElements = new TreeSet(Arrays.asList(o._geometries));
98316               return this.compare(theseElements, otherElements)
98317             } else if (arguments.length === 2) {
98318               var o$1 = arguments[0];
98319               var comp = arguments[1];
98320               var gc = o$1;
98321               var n1 = this.getNumGeometries();
98322               var n2 = gc.getNumGeometries();
98323               var i = 0;
98324               while (i < n1 && i < n2) {
98325                 var thisGeom = this$1.getGeometryN(i);
98326                 var otherGeom = gc.getGeometryN(i);
98327                 var holeComp = thisGeom.compareToSameClass(otherGeom, comp);
98328                 if (holeComp !== 0) { return holeComp }
98329                 i++;
98330               }
98331               if (i < n1) { return 1 }
98332               if (i < n2) { return -1 }
98333               return 0
98334             }
98335           };
98336           GeometryCollection.prototype.apply = function apply () {
98337             var this$1 = this;
98338
98339             if (hasInterface(arguments[0], CoordinateFilter)) {
98340               var filter = arguments[0];
98341               for (var i = 0; i < this._geometries.length; i++) {
98342                 this$1._geometries[i].apply(filter);
98343               }
98344             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98345               var filter$1 = arguments[0];
98346               if (this._geometries.length === 0) { return null }
98347               for (var i$1 = 0; i$1 < this._geometries.length; i$1++) {
98348                 this$1._geometries[i$1].apply(filter$1);
98349                 if (filter$1.isDone()) {
98350                   break
98351                 }
98352               }
98353               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98354             } else if (hasInterface(arguments[0], GeometryFilter)) {
98355               var filter$2 = arguments[0];
98356               filter$2.filter(this);
98357               for (var i$2 = 0; i$2 < this._geometries.length; i$2++) {
98358                 this$1._geometries[i$2].apply(filter$2);
98359               }
98360             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98361               var filter$3 = arguments[0];
98362               filter$3.filter(this);
98363               for (var i$3 = 0; i$3 < this._geometries.length; i$3++) {
98364                 this$1._geometries[i$3].apply(filter$3);
98365               }
98366             }
98367           };
98368           GeometryCollection.prototype.getBoundary = function getBoundary () {
98369             this.checkNotGeometryCollection(this);
98370             Assert.shouldNeverReachHere();
98371             return null
98372           };
98373           GeometryCollection.prototype.clone = function clone () {
98374             var this$1 = this;
98375
98376             var gc = Geometry$$1.prototype.clone.call(this);
98377             gc._geometries = new Array(this._geometries.length).fill(null);
98378             for (var i = 0; i < this._geometries.length; i++) {
98379               gc._geometries[i] = this$1._geometries[i].clone();
98380             }
98381             return gc
98382           };
98383           GeometryCollection.prototype.getGeometryType = function getGeometryType () {
98384             return 'GeometryCollection'
98385           };
98386           GeometryCollection.prototype.copy = function copy () {
98387             var this$1 = this;
98388
98389             var geometries = new Array(this._geometries.length).fill(null);
98390             for (var i = 0; i < geometries.length; i++) {
98391               geometries[i] = this$1._geometries[i].copy();
98392             }
98393             return new GeometryCollection(geometries, this._factory)
98394           };
98395           GeometryCollection.prototype.isEmpty = function isEmpty () {
98396             var this$1 = this;
98397
98398             for (var i = 0; i < this._geometries.length; i++) {
98399               if (!this$1._geometries[i].isEmpty()) {
98400                 return false
98401               }
98402             }
98403             return true
98404           };
98405           GeometryCollection.prototype.interfaces_ = function interfaces_ () {
98406             return []
98407           };
98408           GeometryCollection.prototype.getClass = function getClass () {
98409             return GeometryCollection
98410           };
98411           staticAccessors.serialVersionUID.get = function () { return -5694727726395021467 };
98412
98413           Object.defineProperties( GeometryCollection, staticAccessors );
98414
98415           return GeometryCollection;
98416         }(Geometry));
98417
98418         var MultiLineString = (function (GeometryCollection$$1) {
98419           function MultiLineString () {
98420             GeometryCollection$$1.apply(this, arguments);
98421           }
98422
98423           if ( GeometryCollection$$1 ) { MultiLineString.__proto__ = GeometryCollection$$1; }
98424           MultiLineString.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
98425           MultiLineString.prototype.constructor = MultiLineString;
98426
98427           var staticAccessors = { serialVersionUID: { configurable: true } };
98428
98429           MultiLineString.prototype.getSortIndex = function getSortIndex () {
98430             return Geometry.SORTINDEX_MULTILINESTRING
98431           };
98432           MultiLineString.prototype.equalsExact = function equalsExact () {
98433             if (arguments.length === 2) {
98434               var other = arguments[0];
98435               var tolerance = arguments[1];
98436               if (!this.isEquivalentClass(other)) {
98437                 return false
98438               }
98439               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
98440             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
98441           };
98442           MultiLineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98443             if (this.isClosed()) {
98444               return Dimension.FALSE
98445             }
98446             return 0
98447           };
98448           MultiLineString.prototype.isClosed = function isClosed () {
98449             var this$1 = this;
98450
98451             if (this.isEmpty()) {
98452               return false
98453             }
98454             for (var i = 0; i < this._geometries.length; i++) {
98455               if (!this$1._geometries[i].isClosed()) {
98456                 return false
98457               }
98458             }
98459             return true
98460           };
98461           MultiLineString.prototype.getDimension = function getDimension () {
98462             return 1
98463           };
98464           MultiLineString.prototype.reverse = function reverse () {
98465             var this$1 = this;
98466
98467             var nLines = this._geometries.length;
98468             var revLines = new Array(nLines).fill(null);
98469             for (var i = 0; i < this._geometries.length; i++) {
98470               revLines[nLines - 1 - i] = this$1._geometries[i].reverse();
98471             }
98472             return this.getFactory().createMultiLineString(revLines)
98473           };
98474           MultiLineString.prototype.getBoundary = function getBoundary () {
98475             return new BoundaryOp(this).getBoundary()
98476           };
98477           MultiLineString.prototype.getGeometryType = function getGeometryType () {
98478             return 'MultiLineString'
98479           };
98480           MultiLineString.prototype.copy = function copy () {
98481             var this$1 = this;
98482
98483             var lineStrings = new Array(this._geometries.length).fill(null);
98484             for (var i = 0; i < lineStrings.length; i++) {
98485               lineStrings[i] = this$1._geometries[i].copy();
98486             }
98487             return new MultiLineString(lineStrings, this._factory)
98488           };
98489           MultiLineString.prototype.interfaces_ = function interfaces_ () {
98490             return [Lineal]
98491           };
98492           MultiLineString.prototype.getClass = function getClass () {
98493             return MultiLineString
98494           };
98495           staticAccessors.serialVersionUID.get = function () { return 8166665132445433741 };
98496
98497           Object.defineProperties( MultiLineString, staticAccessors );
98498
98499           return MultiLineString;
98500         }(GeometryCollection));
98501
98502         var BoundaryOp = function BoundaryOp () {
98503           this._geom = null;
98504           this._geomFact = null;
98505           this._bnRule = null;
98506           this._endpointMap = null;
98507           if (arguments.length === 1) {
98508             var geom = arguments[0];
98509             var bnRule = BoundaryNodeRule.MOD2_BOUNDARY_RULE;
98510             this._geom = geom;
98511             this._geomFact = geom.getFactory();
98512             this._bnRule = bnRule;
98513           } else if (arguments.length === 2) {
98514             var geom$1 = arguments[0];
98515             var bnRule$1 = arguments[1];
98516             this._geom = geom$1;
98517             this._geomFact = geom$1.getFactory();
98518             this._bnRule = bnRule$1;
98519           }
98520         };
98521         BoundaryOp.prototype.boundaryMultiLineString = function boundaryMultiLineString (mLine) {
98522           if (this._geom.isEmpty()) {
98523             return this.getEmptyMultiPoint()
98524           }
98525           var bdyPts = this.computeBoundaryCoordinates(mLine);
98526           if (bdyPts.length === 1) {
98527             return this._geomFact.createPoint(bdyPts[0])
98528           }
98529           return this._geomFact.createMultiPointFromCoords(bdyPts)
98530         };
98531         BoundaryOp.prototype.getBoundary = function getBoundary () {
98532           if (this._geom instanceof LineString) { return this.boundaryLineString(this._geom) }
98533           if (this._geom instanceof MultiLineString) { return this.boundaryMultiLineString(this._geom) }
98534           return this._geom.getBoundary()
98535         };
98536         BoundaryOp.prototype.boundaryLineString = function boundaryLineString (line) {
98537           if (this._geom.isEmpty()) {
98538             return this.getEmptyMultiPoint()
98539           }
98540           if (line.isClosed()) {
98541             var closedEndpointOnBoundary = this._bnRule.isInBoundary(2);
98542             if (closedEndpointOnBoundary) {
98543               return line.getStartPoint()
98544             } else {
98545               return this._geomFact.createMultiPoint()
98546             }
98547           }
98548           return this._geomFact.createMultiPoint([line.getStartPoint(), line.getEndPoint()])
98549         };
98550         BoundaryOp.prototype.getEmptyMultiPoint = function getEmptyMultiPoint () {
98551           return this._geomFact.createMultiPoint()
98552         };
98553         BoundaryOp.prototype.computeBoundaryCoordinates = function computeBoundaryCoordinates (mLine) {
98554             var this$1 = this;
98555
98556           var bdyPts = new ArrayList();
98557           this._endpointMap = new TreeMap();
98558           for (var i = 0; i < mLine.getNumGeometries(); i++) {
98559             var line = mLine.getGeometryN(i);
98560             if (line.getNumPoints() === 0) { continue }
98561             this$1.addEndpoint(line.getCoordinateN(0));
98562             this$1.addEndpoint(line.getCoordinateN(line.getNumPoints() - 1));
98563           }
98564           for (var it = this._endpointMap.entrySet().iterator(); it.hasNext();) {
98565             var entry = it.next();
98566             var counter = entry.getValue();
98567             var valence = counter.count;
98568             if (this$1._bnRule.isInBoundary(valence)) {
98569               bdyPts.add(entry.getKey());
98570             }
98571           }
98572           return CoordinateArrays.toCoordinateArray(bdyPts)
98573         };
98574         BoundaryOp.prototype.addEndpoint = function addEndpoint (pt) {
98575           var counter = this._endpointMap.get(pt);
98576           if (counter === null) {
98577             counter = new Counter();
98578             this._endpointMap.put(pt, counter);
98579           }
98580           counter.count++;
98581         };
98582         BoundaryOp.prototype.interfaces_ = function interfaces_ () {
98583           return []
98584         };
98585         BoundaryOp.prototype.getClass = function getClass () {
98586           return BoundaryOp
98587         };
98588         BoundaryOp.getBoundary = function getBoundary () {
98589           if (arguments.length === 1) {
98590             var g = arguments[0];
98591             var bop = new BoundaryOp(g);
98592             return bop.getBoundary()
98593           } else if (arguments.length === 2) {
98594             var g$1 = arguments[0];
98595             var bnRule = arguments[1];
98596             var bop$1 = new BoundaryOp(g$1, bnRule);
98597             return bop$1.getBoundary()
98598           }
98599         };
98600
98601         var Counter = function Counter () {
98602           this.count = null;
98603         };
98604         Counter.prototype.interfaces_ = function interfaces_ () {
98605           return []
98606         };
98607         Counter.prototype.getClass = function getClass () {
98608           return Counter
98609         };
98610
98611         // boundary
98612
98613         function PrintStream () {}
98614
98615         function StringReader () {}
98616
98617         var DecimalFormat = function DecimalFormat () {};
98618
98619         function ByteArrayOutputStream () {}
98620
98621         function IOException () {}
98622
98623         function LineNumberReader () {}
98624
98625         var StringUtil = function StringUtil () {};
98626
98627         var staticAccessors$15 = { NEWLINE: { configurable: true },SIMPLE_ORDINATE_FORMAT: { configurable: true } };
98628
98629         StringUtil.prototype.interfaces_ = function interfaces_ () {
98630           return []
98631         };
98632         StringUtil.prototype.getClass = function getClass () {
98633           return StringUtil
98634         };
98635         StringUtil.chars = function chars (c, n) {
98636           var ch = new Array(n).fill(null);
98637           for (var i = 0; i < n; i++) {
98638             ch[i] = c;
98639           }
98640           return String(ch)
98641         };
98642         StringUtil.getStackTrace = function getStackTrace () {
98643           if (arguments.length === 1) {
98644             var t = arguments[0];
98645             var os = new ByteArrayOutputStream();
98646             var ps = new PrintStream(os);
98647             t.printStackTrace(ps);
98648             return os.toString()
98649           } else if (arguments.length === 2) {
98650             var t$1 = arguments[0];
98651             var depth = arguments[1];
98652             var stackTrace = '';
98653             var stringReader = new StringReader(StringUtil.getStackTrace(t$1));
98654             var lineNumberReader = new LineNumberReader(stringReader);
98655             for (var i = 0; i < depth; i++) {
98656               try {
98657                 stackTrace += lineNumberReader.readLine() + StringUtil.NEWLINE;
98658               } catch (e) {
98659                 if (e instanceof IOException) {
98660                   Assert.shouldNeverReachHere();
98661                 } else { throw e }
98662               } finally {}
98663             }
98664             return stackTrace
98665           }
98666         };
98667         StringUtil.split = function split (s, separator) {
98668           var separatorlen = separator.length;
98669           var tokenList = new ArrayList();
98670           var tmpString = '' + s;
98671           var pos = tmpString.indexOf(separator);
98672           while (pos >= 0) {
98673             var token = tmpString.substring(0, pos);
98674             tokenList.add(token);
98675             tmpString = tmpString.substring(pos + separatorlen);
98676             pos = tmpString.indexOf(separator);
98677           }
98678           if (tmpString.length > 0) { tokenList.add(tmpString); }
98679           var res = new Array(tokenList.size()).fill(null);
98680           for (var i = 0; i < res.length; i++) {
98681             res[i] = tokenList.get(i);
98682           }
98683           return res
98684         };
98685         StringUtil.toString = function toString () {
98686           if (arguments.length === 1) {
98687             var d = arguments[0];
98688             return StringUtil.SIMPLE_ORDINATE_FORMAT.format(d)
98689           }
98690         };
98691         StringUtil.spaces = function spaces (n) {
98692           return StringUtil.chars(' ', n)
98693         };
98694         staticAccessors$15.NEWLINE.get = function () { return System.getProperty('line.separator') };
98695         staticAccessors$15.SIMPLE_ORDINATE_FORMAT.get = function () { return new DecimalFormat('0.#') };
98696
98697         Object.defineProperties( StringUtil, staticAccessors$15 );
98698
98699         var CoordinateSequences = function CoordinateSequences () {};
98700
98701         CoordinateSequences.prototype.interfaces_ = function interfaces_ () {
98702           return []
98703         };
98704         CoordinateSequences.prototype.getClass = function getClass () {
98705           return CoordinateSequences
98706         };
98707         CoordinateSequences.copyCoord = function copyCoord (src, srcPos, dest, destPos) {
98708           var minDim = Math.min(src.getDimension(), dest.getDimension());
98709           for (var dim = 0; dim < minDim; dim++) {
98710             dest.setOrdinate(destPos, dim, src.getOrdinate(srcPos, dim));
98711           }
98712         };
98713         CoordinateSequences.isRing = function isRing (seq) {
98714           var n = seq.size();
98715           if (n === 0) { return true }
98716           if (n <= 3) { return false }
98717           return seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y)
98718         };
98719         CoordinateSequences.isEqual = function isEqual (cs1, cs2) {
98720           var cs1Size = cs1.size();
98721           var cs2Size = cs2.size();
98722           if (cs1Size !== cs2Size) { return false }
98723           var dim = Math.min(cs1.getDimension(), cs2.getDimension());
98724           for (var i = 0; i < cs1Size; i++) {
98725             for (var d = 0; d < dim; d++) {
98726               var v1 = cs1.getOrdinate(i, d);
98727               var v2 = cs2.getOrdinate(i, d);
98728               if (cs1.getOrdinate(i, d) === cs2.getOrdinate(i, d)) { continue }
98729               if (Double.isNaN(v1) && Double.isNaN(v2)) { continue }
98730               return false
98731             }
98732           }
98733           return true
98734         };
98735         CoordinateSequences.extend = function extend (fact, seq, size) {
98736           var newseq = fact.create(size, seq.getDimension());
98737           var n = seq.size();
98738           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98739           if (n > 0) {
98740             for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, n - 1, newseq, i, 1); }
98741           }
98742           return newseq
98743         };
98744         CoordinateSequences.reverse = function reverse (seq) {
98745           var last = seq.size() - 1;
98746           var mid = Math.trunc(last / 2);
98747           for (var i = 0; i <= mid; i++) {
98748             CoordinateSequences.swap(seq, i, last - i);
98749           }
98750         };
98751         CoordinateSequences.swap = function swap (seq, i, j) {
98752           if (i === j) { return null }
98753           for (var dim = 0; dim < seq.getDimension(); dim++) {
98754             var tmp = seq.getOrdinate(i, dim);
98755             seq.setOrdinate(i, dim, seq.getOrdinate(j, dim));
98756             seq.setOrdinate(j, dim, tmp);
98757           }
98758         };
98759         CoordinateSequences.copy = function copy (src, srcPos, dest, destPos, length) {
98760           for (var i = 0; i < length; i++) {
98761             CoordinateSequences.copyCoord(src, srcPos + i, dest, destPos + i);
98762           }
98763         };
98764         CoordinateSequences.toString = function toString () {
98765           if (arguments.length === 1) {
98766             var cs = arguments[0];
98767             var size = cs.size();
98768             if (size === 0) { return '()' }
98769             var dim = cs.getDimension();
98770             var buf = new StringBuffer();
98771             buf.append('(');
98772             for (var i = 0; i < size; i++) {
98773               if (i > 0) { buf.append(' '); }
98774               for (var d = 0; d < dim; d++) {
98775                 if (d > 0) { buf.append(','); }
98776                 buf.append(StringUtil.toString(cs.getOrdinate(i, d)));
98777               }
98778             }
98779             buf.append(')');
98780             return buf.toString()
98781           }
98782         };
98783         CoordinateSequences.ensureValidRing = function ensureValidRing (fact, seq) {
98784           var n = seq.size();
98785           if (n === 0) { return seq }
98786           if (n <= 3) { return CoordinateSequences.createClosedRing(fact, seq, 4) }
98787           var isClosed = seq.getOrdinate(0, CoordinateSequence.X) === seq.getOrdinate(n - 1, CoordinateSequence.X) && seq.getOrdinate(0, CoordinateSequence.Y) === seq.getOrdinate(n - 1, CoordinateSequence.Y);
98788           if (isClosed) { return seq }
98789           return CoordinateSequences.createClosedRing(fact, seq, n + 1)
98790         };
98791         CoordinateSequences.createClosedRing = function createClosedRing (fact, seq, size) {
98792           var newseq = fact.create(size, seq.getDimension());
98793           var n = seq.size();
98794           CoordinateSequences.copy(seq, 0, newseq, 0, n);
98795           for (var i = n; i < size; i++) { CoordinateSequences.copy(seq, 0, newseq, i, 1); }
98796           return newseq
98797         };
98798
98799         var LineString = (function (Geometry$$1) {
98800           function LineString (points, factory) {
98801             Geometry$$1.call(this, factory);
98802             this._points = null;
98803             this.init(points);
98804           }
98805
98806           if ( Geometry$$1 ) { LineString.__proto__ = Geometry$$1; }
98807           LineString.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
98808           LineString.prototype.constructor = LineString;
98809
98810           var staticAccessors = { serialVersionUID: { configurable: true } };
98811           LineString.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
98812             if (this.isEmpty()) {
98813               return new Envelope()
98814             }
98815             return this._points.expandEnvelope(new Envelope())
98816           };
98817           LineString.prototype.isRing = function isRing () {
98818             return this.isClosed() && this.isSimple()
98819           };
98820           LineString.prototype.getSortIndex = function getSortIndex () {
98821             return Geometry$$1.SORTINDEX_LINESTRING
98822           };
98823           LineString.prototype.getCoordinates = function getCoordinates () {
98824             return this._points.toCoordinateArray()
98825           };
98826           LineString.prototype.equalsExact = function equalsExact () {
98827             var this$1 = this;
98828
98829             if (arguments.length === 2) {
98830               var other = arguments[0];
98831               var tolerance = arguments[1];
98832               if (!this.isEquivalentClass(other)) {
98833                 return false
98834               }
98835               var otherLineString = other;
98836               if (this._points.size() !== otherLineString._points.size()) {
98837                 return false
98838               }
98839               for (var i = 0; i < this._points.size(); i++) {
98840                 if (!this$1.equal(this$1._points.getCoordinate(i), otherLineString._points.getCoordinate(i), tolerance)) {
98841                   return false
98842                 }
98843               }
98844               return true
98845             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
98846           };
98847           LineString.prototype.normalize = function normalize () {
98848             var this$1 = this;
98849
98850             for (var i = 0; i < Math.trunc(this._points.size() / 2); i++) {
98851               var j = this$1._points.size() - 1 - i;
98852               if (!this$1._points.getCoordinate(i).equals(this$1._points.getCoordinate(j))) {
98853                 if (this$1._points.getCoordinate(i).compareTo(this$1._points.getCoordinate(j)) > 0) {
98854                   CoordinateSequences.reverse(this$1._points);
98855                 }
98856                 return null
98857               }
98858             }
98859           };
98860           LineString.prototype.getCoordinate = function getCoordinate () {
98861             if (this.isEmpty()) { return null }
98862             return this._points.getCoordinate(0)
98863           };
98864           LineString.prototype.getBoundaryDimension = function getBoundaryDimension () {
98865             if (this.isClosed()) {
98866               return Dimension.FALSE
98867             }
98868             return 0
98869           };
98870           LineString.prototype.isClosed = function isClosed () {
98871             if (this.isEmpty()) {
98872               return false
98873             }
98874             return this.getCoordinateN(0).equals2D(this.getCoordinateN(this.getNumPoints() - 1))
98875           };
98876           LineString.prototype.getEndPoint = function getEndPoint () {
98877             if (this.isEmpty()) {
98878               return null
98879             }
98880             return this.getPointN(this.getNumPoints() - 1)
98881           };
98882           LineString.prototype.getDimension = function getDimension () {
98883             return 1
98884           };
98885           LineString.prototype.getLength = function getLength () {
98886             return CGAlgorithms.computeLength(this._points)
98887           };
98888           LineString.prototype.getNumPoints = function getNumPoints () {
98889             return this._points.size()
98890           };
98891           LineString.prototype.reverse = function reverse () {
98892             var seq = this._points.copy();
98893             CoordinateSequences.reverse(seq);
98894             var revLine = this.getFactory().createLineString(seq);
98895             return revLine
98896           };
98897           LineString.prototype.compareToSameClass = function compareToSameClass () {
98898             var this$1 = this;
98899
98900             if (arguments.length === 1) {
98901               var o = arguments[0];
98902               var line = o;
98903               var i = 0;
98904               var j = 0;
98905               while (i < this._points.size() && j < line._points.size()) {
98906                 var comparison = this$1._points.getCoordinate(i).compareTo(line._points.getCoordinate(j));
98907                 if (comparison !== 0) {
98908                   return comparison
98909                 }
98910                 i++;
98911                 j++;
98912               }
98913               if (i < this._points.size()) {
98914                 return 1
98915               }
98916               if (j < line._points.size()) {
98917                 return -1
98918               }
98919               return 0
98920             } else if (arguments.length === 2) {
98921               var o$1 = arguments[0];
98922               var comp = arguments[1];
98923               var line$1 = o$1;
98924               return comp.compare(this._points, line$1._points)
98925             }
98926           };
98927           LineString.prototype.apply = function apply () {
98928             var this$1 = this;
98929
98930             if (hasInterface(arguments[0], CoordinateFilter)) {
98931               var filter = arguments[0];
98932               for (var i = 0; i < this._points.size(); i++) {
98933                 filter.filter(this$1._points.getCoordinate(i));
98934               }
98935             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
98936               var filter$1 = arguments[0];
98937               if (this._points.size() === 0) { return null }
98938               for (var i$1 = 0; i$1 < this._points.size(); i$1++) {
98939                 filter$1.filter(this$1._points, i$1);
98940                 if (filter$1.isDone()) { break }
98941               }
98942               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
98943             } else if (hasInterface(arguments[0], GeometryFilter)) {
98944               var filter$2 = arguments[0];
98945               filter$2.filter(this);
98946             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
98947               var filter$3 = arguments[0];
98948               filter$3.filter(this);
98949             }
98950           };
98951           LineString.prototype.getBoundary = function getBoundary () {
98952             return new BoundaryOp(this).getBoundary()
98953           };
98954           LineString.prototype.isEquivalentClass = function isEquivalentClass (other) {
98955             return other instanceof LineString
98956           };
98957           LineString.prototype.clone = function clone () {
98958             var ls = Geometry$$1.prototype.clone.call(this);
98959             ls._points = this._points.clone();
98960             return ls
98961           };
98962           LineString.prototype.getCoordinateN = function getCoordinateN (n) {
98963             return this._points.getCoordinate(n)
98964           };
98965           LineString.prototype.getGeometryType = function getGeometryType () {
98966             return 'LineString'
98967           };
98968           LineString.prototype.copy = function copy () {
98969             return new LineString(this._points.copy(), this._factory)
98970           };
98971           LineString.prototype.getCoordinateSequence = function getCoordinateSequence () {
98972             return this._points
98973           };
98974           LineString.prototype.isEmpty = function isEmpty () {
98975             return this._points.size() === 0
98976           };
98977           LineString.prototype.init = function init (points) {
98978             if (points === null) {
98979               points = this.getFactory().getCoordinateSequenceFactory().create([]);
98980             }
98981             if (points.size() === 1) {
98982               throw new IllegalArgumentException('Invalid number of points in LineString (found ' + points.size() + ' - must be 0 or >= 2)')
98983             }
98984             this._points = points;
98985           };
98986           LineString.prototype.isCoordinate = function isCoordinate (pt) {
98987             var this$1 = this;
98988
98989             for (var i = 0; i < this._points.size(); i++) {
98990               if (this$1._points.getCoordinate(i).equals(pt)) {
98991                 return true
98992               }
98993             }
98994             return false
98995           };
98996           LineString.prototype.getStartPoint = function getStartPoint () {
98997             if (this.isEmpty()) {
98998               return null
98999             }
99000             return this.getPointN(0)
99001           };
99002           LineString.prototype.getPointN = function getPointN (n) {
99003             return this.getFactory().createPoint(this._points.getCoordinate(n))
99004           };
99005           LineString.prototype.interfaces_ = function interfaces_ () {
99006             return [Lineal]
99007           };
99008           LineString.prototype.getClass = function getClass () {
99009             return LineString
99010           };
99011           staticAccessors.serialVersionUID.get = function () { return 3110669828065365560 };
99012
99013           Object.defineProperties( LineString, staticAccessors );
99014
99015           return LineString;
99016         }(Geometry));
99017
99018         var Puntal = function Puntal () {};
99019
99020         Puntal.prototype.interfaces_ = function interfaces_ () {
99021           return []
99022         };
99023         Puntal.prototype.getClass = function getClass () {
99024           return Puntal
99025         };
99026
99027         var Point$1 = (function (Geometry$$1) {
99028           function Point (coordinates, factory) {
99029             Geometry$$1.call(this, factory);
99030             this._coordinates = coordinates || null;
99031             this.init(this._coordinates);
99032           }
99033
99034           if ( Geometry$$1 ) { Point.__proto__ = Geometry$$1; }
99035           Point.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99036           Point.prototype.constructor = Point;
99037
99038           var staticAccessors = { serialVersionUID: { configurable: true } };
99039           Point.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99040             if (this.isEmpty()) {
99041               return new Envelope()
99042             }
99043             var env = new Envelope();
99044             env.expandToInclude(this._coordinates.getX(0), this._coordinates.getY(0));
99045             return env
99046           };
99047           Point.prototype.getSortIndex = function getSortIndex () {
99048             return Geometry$$1.SORTINDEX_POINT
99049           };
99050           Point.prototype.getCoordinates = function getCoordinates () {
99051             return this.isEmpty() ? [] : [this.getCoordinate()]
99052           };
99053           Point.prototype.equalsExact = function equalsExact () {
99054             if (arguments.length === 2) {
99055               var other = arguments[0];
99056               var tolerance = arguments[1];
99057               if (!this.isEquivalentClass(other)) {
99058                 return false
99059               }
99060               if (this.isEmpty() && other.isEmpty()) {
99061                 return true
99062               }
99063               if (this.isEmpty() !== other.isEmpty()) {
99064                 return false
99065               }
99066               return this.equal(other.getCoordinate(), this.getCoordinate(), tolerance)
99067             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99068           };
99069           Point.prototype.normalize = function normalize () {};
99070           Point.prototype.getCoordinate = function getCoordinate () {
99071             return this._coordinates.size() !== 0 ? this._coordinates.getCoordinate(0) : null
99072           };
99073           Point.prototype.getBoundaryDimension = function getBoundaryDimension () {
99074             return Dimension.FALSE
99075           };
99076           Point.prototype.getDimension = function getDimension () {
99077             return 0
99078           };
99079           Point.prototype.getNumPoints = function getNumPoints () {
99080             return this.isEmpty() ? 0 : 1
99081           };
99082           Point.prototype.reverse = function reverse () {
99083             return this.copy()
99084           };
99085           Point.prototype.getX = function getX () {
99086             if (this.getCoordinate() === null) {
99087               throw new Error('getX called on empty Point')
99088             }
99089             return this.getCoordinate().x
99090           };
99091           Point.prototype.compareToSameClass = function compareToSameClass () {
99092             if (arguments.length === 1) {
99093               var other = arguments[0];
99094               var point$1 = other;
99095               return this.getCoordinate().compareTo(point$1.getCoordinate())
99096             } else if (arguments.length === 2) {
99097               var other$1 = arguments[0];
99098               var comp = arguments[1];
99099               var point = other$1;
99100               return comp.compare(this._coordinates, point._coordinates)
99101             }
99102           };
99103           Point.prototype.apply = function apply () {
99104             if (hasInterface(arguments[0], CoordinateFilter)) {
99105               var filter = arguments[0];
99106               if (this.isEmpty()) {
99107                 return null
99108               }
99109               filter.filter(this.getCoordinate());
99110             } else if (hasInterface(arguments[0], CoordinateSequenceFilter)) {
99111               var filter$1 = arguments[0];
99112               if (this.isEmpty()) { return null }
99113               filter$1.filter(this._coordinates, 0);
99114               if (filter$1.isGeometryChanged()) { this.geometryChanged(); }
99115             } else if (hasInterface(arguments[0], GeometryFilter)) {
99116               var filter$2 = arguments[0];
99117               filter$2.filter(this);
99118             } else if (hasInterface(arguments[0], GeometryComponentFilter)) {
99119               var filter$3 = arguments[0];
99120               filter$3.filter(this);
99121             }
99122           };
99123           Point.prototype.getBoundary = function getBoundary () {
99124             return this.getFactory().createGeometryCollection(null)
99125           };
99126           Point.prototype.clone = function clone () {
99127             var p = Geometry$$1.prototype.clone.call(this);
99128             p._coordinates = this._coordinates.clone();
99129             return p
99130           };
99131           Point.prototype.getGeometryType = function getGeometryType () {
99132             return 'Point'
99133           };
99134           Point.prototype.copy = function copy () {
99135             return new Point(this._coordinates.copy(), this._factory)
99136           };
99137           Point.prototype.getCoordinateSequence = function getCoordinateSequence () {
99138             return this._coordinates
99139           };
99140           Point.prototype.getY = function getY () {
99141             if (this.getCoordinate() === null) {
99142               throw new Error('getY called on empty Point')
99143             }
99144             return this.getCoordinate().y
99145           };
99146           Point.prototype.isEmpty = function isEmpty () {
99147             return this._coordinates.size() === 0
99148           };
99149           Point.prototype.init = function init (coordinates) {
99150             if (coordinates === null) {
99151               coordinates = this.getFactory().getCoordinateSequenceFactory().create([]);
99152             }
99153             Assert.isTrue(coordinates.size() <= 1);
99154             this._coordinates = coordinates;
99155           };
99156           Point.prototype.isSimple = function isSimple () {
99157             return true
99158           };
99159           Point.prototype.interfaces_ = function interfaces_ () {
99160             return [Puntal]
99161           };
99162           Point.prototype.getClass = function getClass () {
99163             return Point
99164           };
99165           staticAccessors.serialVersionUID.get = function () { return 4902022702746614570 };
99166
99167           Object.defineProperties( Point, staticAccessors );
99168
99169           return Point;
99170         }(Geometry));
99171
99172         var Polygonal = function Polygonal () {};
99173
99174         Polygonal.prototype.interfaces_ = function interfaces_ () {
99175           return []
99176         };
99177         Polygonal.prototype.getClass = function getClass () {
99178           return Polygonal
99179         };
99180
99181         var Polygon = (function (Geometry$$1) {
99182           function Polygon (shell, holes, factory) {
99183             Geometry$$1.call(this, factory);
99184             this._shell = null;
99185             this._holes = null;
99186             if (shell === null) {
99187               shell = this.getFactory().createLinearRing();
99188             }
99189             if (holes === null) {
99190               holes = [];
99191             }
99192             if (Geometry$$1.hasNullElements(holes)) {
99193               throw new IllegalArgumentException('holes must not contain null elements')
99194             }
99195             if (shell.isEmpty() && Geometry$$1.hasNonEmptyElements(holes)) {
99196               throw new IllegalArgumentException('shell is empty but holes are not')
99197             }
99198             this._shell = shell;
99199             this._holes = holes;
99200           }
99201
99202           if ( Geometry$$1 ) { Polygon.__proto__ = Geometry$$1; }
99203           Polygon.prototype = Object.create( Geometry$$1 && Geometry$$1.prototype );
99204           Polygon.prototype.constructor = Polygon;
99205
99206           var staticAccessors = { serialVersionUID: { configurable: true } };
99207           Polygon.prototype.computeEnvelopeInternal = function computeEnvelopeInternal () {
99208             return this._shell.getEnvelopeInternal()
99209           };
99210           Polygon.prototype.getSortIndex = function getSortIndex () {
99211             return Geometry$$1.SORTINDEX_POLYGON
99212           };
99213           Polygon.prototype.getCoordinates = function getCoordinates () {
99214             var this$1 = this;
99215
99216             if (this.isEmpty()) {
99217               return []
99218             }
99219             var coordinates = new Array(this.getNumPoints()).fill(null);
99220             var k = -1;
99221             var shellCoordinates = this._shell.getCoordinates();
99222             for (var x = 0; x < shellCoordinates.length; x++) {
99223               k++;
99224               coordinates[k] = shellCoordinates[x];
99225             }
99226             for (var i = 0; i < this._holes.length; i++) {
99227               var childCoordinates = this$1._holes[i].getCoordinates();
99228               for (var j = 0; j < childCoordinates.length; j++) {
99229                 k++;
99230                 coordinates[k] = childCoordinates[j];
99231               }
99232             }
99233             return coordinates
99234           };
99235           Polygon.prototype.getArea = function getArea () {
99236             var this$1 = this;
99237
99238             var area = 0.0;
99239             area += Math.abs(CGAlgorithms.signedArea(this._shell.getCoordinateSequence()));
99240             for (var i = 0; i < this._holes.length; i++) {
99241               area -= Math.abs(CGAlgorithms.signedArea(this$1._holes[i].getCoordinateSequence()));
99242             }
99243             return area
99244           };
99245           Polygon.prototype.isRectangle = function isRectangle () {
99246             if (this.getNumInteriorRing() !== 0) { return false }
99247             if (this._shell === null) { return false }
99248             if (this._shell.getNumPoints() !== 5) { return false }
99249             var seq = this._shell.getCoordinateSequence();
99250             var env = this.getEnvelopeInternal();
99251             for (var i = 0; i < 5; i++) {
99252               var x = seq.getX(i);
99253               if (!(x === env.getMinX() || x === env.getMaxX())) { return false }
99254               var y = seq.getY(i);
99255               if (!(y === env.getMinY() || y === env.getMaxY())) { return false }
99256             }
99257             var prevX = seq.getX(0);
99258             var prevY = seq.getY(0);
99259             for (var i$1 = 1; i$1 <= 4; i$1++) {
99260               var x$1 = seq.getX(i$1);
99261               var y$1 = seq.getY(i$1);
99262               var xChanged = x$1 !== prevX;
99263               var yChanged = y$1 !== prevY;
99264               if (xChanged === yChanged) { return false }
99265               prevX = x$1;
99266               prevY = y$1;
99267             }
99268             return true
99269           };
99270           Polygon.prototype.equalsExact = function equalsExact () {
99271             var this$1 = this;
99272
99273             if (arguments.length === 2) {
99274               var other = arguments[0];
99275               var tolerance = arguments[1];
99276               if (!this.isEquivalentClass(other)) {
99277                 return false
99278               }
99279               var otherPolygon = other;
99280               var thisShell = this._shell;
99281               var otherPolygonShell = otherPolygon._shell;
99282               if (!thisShell.equalsExact(otherPolygonShell, tolerance)) {
99283                 return false
99284               }
99285               if (this._holes.length !== otherPolygon._holes.length) {
99286                 return false
99287               }
99288               for (var i = 0; i < this._holes.length; i++) {
99289                 if (!this$1._holes[i].equalsExact(otherPolygon._holes[i], tolerance)) {
99290                   return false
99291                 }
99292               }
99293               return true
99294             } else { return Geometry$$1.prototype.equalsExact.apply(this, arguments) }
99295           };
99296           Polygon.prototype.normalize = function normalize () {
99297             var this$1 = this;
99298
99299             if (arguments.length === 0) {
99300               this.normalize(this._shell, true);
99301               for (var i = 0; i < this._holes.length; i++) {
99302                 this$1.normalize(this$1._holes[i], false);
99303               }
99304               Arrays.sort(this._holes);
99305             } else if (arguments.length === 2) {
99306               var ring = arguments[0];
99307               var clockwise = arguments[1];
99308               if (ring.isEmpty()) {
99309                 return null
99310               }
99311               var uniqueCoordinates = new Array(ring.getCoordinates().length - 1).fill(null);
99312               System.arraycopy(ring.getCoordinates(), 0, uniqueCoordinates, 0, uniqueCoordinates.length);
99313               var minCoordinate = CoordinateArrays.minCoordinate(ring.getCoordinates());
99314               CoordinateArrays.scroll(uniqueCoordinates, minCoordinate);
99315               System.arraycopy(uniqueCoordinates, 0, ring.getCoordinates(), 0, uniqueCoordinates.length);
99316               ring.getCoordinates()[uniqueCoordinates.length] = uniqueCoordinates[0];
99317               if (CGAlgorithms.isCCW(ring.getCoordinates()) === clockwise) {
99318                 CoordinateArrays.reverse(ring.getCoordinates());
99319               }
99320             }
99321           };
99322           Polygon.prototype.getCoordinate = function getCoordinate () {
99323             return this._shell.getCoordinate()
99324           };
99325           Polygon.prototype.getNumInteriorRing = function getNumInteriorRing () {
99326             return this._holes.length
99327           };
99328           Polygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99329             return 1
99330           };
99331           Polygon.prototype.getDimension = function getDimension () {
99332             return 2
99333           };
99334           Polygon.prototype.getLength = function getLength () {
99335             var this$1 = this;
99336
99337             var len = 0.0;
99338             len += this._shell.getLength();
99339             for (var i = 0; i < this._holes.length; i++) {
99340               len += this$1._holes[i].getLength();
99341             }
99342             return len
99343           };
99344           Polygon.prototype.getNumPoints = function getNumPoints () {
99345             var this$1 = this;
99346
99347             var numPoints = this._shell.getNumPoints();
99348             for (var i = 0; i < this._holes.length; i++) {
99349               numPoints += this$1._holes[i].getNumPoints();
99350             }
99351             return numPoints
99352           };
99353           Polygon.prototype.reverse = function reverse () {
99354             var this$1 = this;
99355
99356             var poly = this.copy();
99357             poly._shell = this._shell.copy().reverse();
99358             poly._holes = new Array(this._holes.length).fill(null);
99359             for (var i = 0; i < this._holes.length; i++) {
99360               poly._holes[i] = this$1._holes[i].copy().reverse();
99361             }
99362             return poly
99363           };
99364           Polygon.prototype.convexHull = function convexHull () {
99365             return this.getExteriorRing().convexHull()
99366           };
99367           Polygon.prototype.compareToSameClass = function compareToSameClass () {
99368             var this$1 = this;
99369
99370             if (arguments.length === 1) {
99371               var o = arguments[0];
99372               var thisShell = this._shell;
99373               var otherShell = o._shell;
99374               return thisShell.compareToSameClass(otherShell)
99375             } else if (arguments.length === 2) {
99376               var o$1 = arguments[0];
99377               var comp = arguments[1];
99378               var poly = o$1;
99379               var thisShell$1 = this._shell;
99380               var otherShell$1 = poly._shell;
99381               var shellComp = thisShell$1.compareToSameClass(otherShell$1, comp);
99382               if (shellComp !== 0) { return shellComp }
99383               var nHole1 = this.getNumInteriorRing();
99384               var nHole2 = poly.getNumInteriorRing();
99385               var i = 0;
99386               while (i < nHole1 && i < nHole2) {
99387                 var thisHole = this$1.getInteriorRingN(i);
99388                 var otherHole = poly.getInteriorRingN(i);
99389                 var holeComp = thisHole.compareToSameClass(otherHole, comp);
99390                 if (holeComp !== 0) { return holeComp }
99391                 i++;
99392               }
99393               if (i < nHole1) { return 1 }
99394               if (i < nHole2) { return -1 }
99395               return 0
99396             }
99397           };
99398           Polygon.prototype.apply = function apply (filter) {
99399             var this$1 = this;
99400
99401             if (hasInterface(filter, CoordinateFilter)) {
99402               this._shell.apply(filter);
99403               for (var i$1 = 0; i$1 < this._holes.length; i$1++) {
99404                 this$1._holes[i$1].apply(filter);
99405               }
99406             } else if (hasInterface(filter, CoordinateSequenceFilter)) {
99407               this._shell.apply(filter);
99408               if (!filter.isDone()) {
99409                 for (var i$2 = 0; i$2 < this._holes.length; i$2++) {
99410                   this$1._holes[i$2].apply(filter);
99411                   if (filter.isDone()) { break }
99412                 }
99413               }
99414               if (filter.isGeometryChanged()) { this.geometryChanged(); }
99415             } else if (hasInterface(filter, GeometryFilter)) {
99416               filter.filter(this);
99417             } else if (hasInterface(filter, GeometryComponentFilter)) {
99418               filter.filter(this);
99419               this._shell.apply(filter);
99420               for (var i = 0; i < this._holes.length; i++) {
99421                 this$1._holes[i].apply(filter);
99422               }
99423             }
99424           };
99425           Polygon.prototype.getBoundary = function getBoundary () {
99426             var this$1 = this;
99427
99428             if (this.isEmpty()) {
99429               return this.getFactory().createMultiLineString()
99430             }
99431             var rings = new Array(this._holes.length + 1).fill(null);
99432             rings[0] = this._shell;
99433             for (var i = 0; i < this._holes.length; i++) {
99434               rings[i + 1] = this$1._holes[i];
99435             }
99436             if (rings.length <= 1) { return this.getFactory().createLinearRing(rings[0].getCoordinateSequence()) }
99437             return this.getFactory().createMultiLineString(rings)
99438           };
99439           Polygon.prototype.clone = function clone () {
99440             var this$1 = this;
99441
99442             var poly = Geometry$$1.prototype.clone.call(this);
99443             poly._shell = this._shell.clone();
99444             poly._holes = new Array(this._holes.length).fill(null);
99445             for (var i = 0; i < this._holes.length; i++) {
99446               poly._holes[i] = this$1._holes[i].clone();
99447             }
99448             return poly
99449           };
99450           Polygon.prototype.getGeometryType = function getGeometryType () {
99451             return 'Polygon'
99452           };
99453           Polygon.prototype.copy = function copy () {
99454             var this$1 = this;
99455
99456             var shell = this._shell.copy();
99457             var holes = new Array(this._holes.length).fill(null);
99458             for (var i = 0; i < holes.length; i++) {
99459               holes[i] = this$1._holes[i].copy();
99460             }
99461             return new Polygon(shell, holes, this._factory)
99462           };
99463           Polygon.prototype.getExteriorRing = function getExteriorRing () {
99464             return this._shell
99465           };
99466           Polygon.prototype.isEmpty = function isEmpty () {
99467             return this._shell.isEmpty()
99468           };
99469           Polygon.prototype.getInteriorRingN = function getInteriorRingN (n) {
99470             return this._holes[n]
99471           };
99472           Polygon.prototype.interfaces_ = function interfaces_ () {
99473             return [Polygonal]
99474           };
99475           Polygon.prototype.getClass = function getClass () {
99476             return Polygon
99477           };
99478           staticAccessors.serialVersionUID.get = function () { return -3494792200821764533 };
99479
99480           Object.defineProperties( Polygon, staticAccessors );
99481
99482           return Polygon;
99483         }(Geometry));
99484
99485         var MultiPoint = (function (GeometryCollection$$1) {
99486           function MultiPoint () {
99487             GeometryCollection$$1.apply(this, arguments);
99488           }
99489
99490           if ( GeometryCollection$$1 ) { MultiPoint.__proto__ = GeometryCollection$$1; }
99491           MultiPoint.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99492           MultiPoint.prototype.constructor = MultiPoint;
99493
99494           var staticAccessors = { serialVersionUID: { configurable: true } };
99495
99496           MultiPoint.prototype.getSortIndex = function getSortIndex () {
99497             return Geometry.SORTINDEX_MULTIPOINT
99498           };
99499           MultiPoint.prototype.isValid = function isValid () {
99500             return true
99501           };
99502           MultiPoint.prototype.equalsExact = function equalsExact () {
99503             if (arguments.length === 2) {
99504               var other = arguments[0];
99505               var tolerance = arguments[1];
99506               if (!this.isEquivalentClass(other)) {
99507                 return false
99508               }
99509               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99510             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99511           };
99512           MultiPoint.prototype.getCoordinate = function getCoordinate () {
99513             if (arguments.length === 1) {
99514               var n = arguments[0];
99515               return this._geometries[n].getCoordinate()
99516             } else { return GeometryCollection$$1.prototype.getCoordinate.apply(this, arguments) }
99517           };
99518           MultiPoint.prototype.getBoundaryDimension = function getBoundaryDimension () {
99519             return Dimension.FALSE
99520           };
99521           MultiPoint.prototype.getDimension = function getDimension () {
99522             return 0
99523           };
99524           MultiPoint.prototype.getBoundary = function getBoundary () {
99525             return this.getFactory().createGeometryCollection(null)
99526           };
99527           MultiPoint.prototype.getGeometryType = function getGeometryType () {
99528             return 'MultiPoint'
99529           };
99530           MultiPoint.prototype.copy = function copy () {
99531             var this$1 = this;
99532
99533             var points = new Array(this._geometries.length).fill(null);
99534             for (var i = 0; i < points.length; i++) {
99535               points[i] = this$1._geometries[i].copy();
99536             }
99537             return new MultiPoint(points, this._factory)
99538           };
99539           MultiPoint.prototype.interfaces_ = function interfaces_ () {
99540             return [Puntal]
99541           };
99542           MultiPoint.prototype.getClass = function getClass () {
99543             return MultiPoint
99544           };
99545           staticAccessors.serialVersionUID.get = function () { return -8048474874175355449 };
99546
99547           Object.defineProperties( MultiPoint, staticAccessors );
99548
99549           return MultiPoint;
99550         }(GeometryCollection));
99551
99552         var LinearRing = (function (LineString$$1) {
99553           function LinearRing (points, factory) {
99554             if (points instanceof Coordinate && factory instanceof GeometryFactory) {
99555               points = factory.getCoordinateSequenceFactory().create(points);
99556             }
99557             LineString$$1.call(this, points, factory);
99558             this.validateConstruction();
99559           }
99560
99561           if ( LineString$$1 ) { LinearRing.__proto__ = LineString$$1; }
99562           LinearRing.prototype = Object.create( LineString$$1 && LineString$$1.prototype );
99563           LinearRing.prototype.constructor = LinearRing;
99564
99565           var staticAccessors = { MINIMUM_VALID_SIZE: { configurable: true },serialVersionUID: { configurable: true } };
99566           LinearRing.prototype.getSortIndex = function getSortIndex () {
99567             return Geometry.SORTINDEX_LINEARRING
99568           };
99569           LinearRing.prototype.getBoundaryDimension = function getBoundaryDimension () {
99570             return Dimension.FALSE
99571           };
99572           LinearRing.prototype.isClosed = function isClosed () {
99573             if (this.isEmpty()) {
99574               return true
99575             }
99576             return LineString$$1.prototype.isClosed.call(this)
99577           };
99578           LinearRing.prototype.reverse = function reverse () {
99579             var seq = this._points.copy();
99580             CoordinateSequences.reverse(seq);
99581             var rev = this.getFactory().createLinearRing(seq);
99582             return rev
99583           };
99584           LinearRing.prototype.validateConstruction = function validateConstruction () {
99585             if (!this.isEmpty() && !LineString$$1.prototype.isClosed.call(this)) {
99586               throw new IllegalArgumentException('Points of LinearRing do not form a closed linestring')
99587             }
99588             if (this.getCoordinateSequence().size() >= 1 && this.getCoordinateSequence().size() < LinearRing.MINIMUM_VALID_SIZE) {
99589               throw new IllegalArgumentException('Invalid number of points in LinearRing (found ' + this.getCoordinateSequence().size() + ' - must be 0 or >= 4)')
99590             }
99591           };
99592           LinearRing.prototype.getGeometryType = function getGeometryType () {
99593             return 'LinearRing'
99594           };
99595           LinearRing.prototype.copy = function copy () {
99596             return new LinearRing(this._points.copy(), this._factory)
99597           };
99598           LinearRing.prototype.interfaces_ = function interfaces_ () {
99599             return []
99600           };
99601           LinearRing.prototype.getClass = function getClass () {
99602             return LinearRing
99603           };
99604           staticAccessors.MINIMUM_VALID_SIZE.get = function () { return 4 };
99605           staticAccessors.serialVersionUID.get = function () { return -4261142084085851829 };
99606
99607           Object.defineProperties( LinearRing, staticAccessors );
99608
99609           return LinearRing;
99610         }(LineString));
99611
99612         var MultiPolygon = (function (GeometryCollection$$1) {
99613           function MultiPolygon () {
99614             GeometryCollection$$1.apply(this, arguments);
99615           }
99616
99617           if ( GeometryCollection$$1 ) { MultiPolygon.__proto__ = GeometryCollection$$1; }
99618           MultiPolygon.prototype = Object.create( GeometryCollection$$1 && GeometryCollection$$1.prototype );
99619           MultiPolygon.prototype.constructor = MultiPolygon;
99620
99621           var staticAccessors = { serialVersionUID: { configurable: true } };
99622
99623           MultiPolygon.prototype.getSortIndex = function getSortIndex () {
99624             return Geometry.SORTINDEX_MULTIPOLYGON
99625           };
99626           MultiPolygon.prototype.equalsExact = function equalsExact () {
99627             if (arguments.length === 2) {
99628               var other = arguments[0];
99629               var tolerance = arguments[1];
99630               if (!this.isEquivalentClass(other)) {
99631                 return false
99632               }
99633               return GeometryCollection$$1.prototype.equalsExact.call(this, other, tolerance)
99634             } else { return GeometryCollection$$1.prototype.equalsExact.apply(this, arguments) }
99635           };
99636           MultiPolygon.prototype.getBoundaryDimension = function getBoundaryDimension () {
99637             return 1
99638           };
99639           MultiPolygon.prototype.getDimension = function getDimension () {
99640             return 2
99641           };
99642           MultiPolygon.prototype.reverse = function reverse () {
99643             var this$1 = this;
99644
99645             var n = this._geometries.length;
99646             var revGeoms = new Array(n).fill(null);
99647             for (var i = 0; i < this._geometries.length; i++) {
99648               revGeoms[i] = this$1._geometries[i].reverse();
99649             }
99650             return this.getFactory().createMultiPolygon(revGeoms)
99651           };
99652           MultiPolygon.prototype.getBoundary = function getBoundary () {
99653             var this$1 = this;
99654
99655             if (this.isEmpty()) {
99656               return this.getFactory().createMultiLineString()
99657             }
99658             var allRings = new ArrayList();
99659             for (var i = 0; i < this._geometries.length; i++) {
99660               var polygon = this$1._geometries[i];
99661               var rings = polygon.getBoundary();
99662               for (var j = 0; j < rings.getNumGeometries(); j++) {
99663                 allRings.add(rings.getGeometryN(j));
99664               }
99665             }
99666             var allRingsArray = new Array(allRings.size()).fill(null);
99667             return this.getFactory().createMultiLineString(allRings.toArray(allRingsArray))
99668           };
99669           MultiPolygon.prototype.getGeometryType = function getGeometryType () {
99670             return 'MultiPolygon'
99671           };
99672           MultiPolygon.prototype.copy = function copy () {
99673             var this$1 = this;
99674
99675             var polygons = new Array(this._geometries.length).fill(null);
99676             for (var i = 0; i < polygons.length; i++) {
99677               polygons[i] = this$1._geometries[i].copy();
99678             }
99679             return new MultiPolygon(polygons, this._factory)
99680           };
99681           MultiPolygon.prototype.interfaces_ = function interfaces_ () {
99682             return [Polygonal]
99683           };
99684           MultiPolygon.prototype.getClass = function getClass () {
99685             return MultiPolygon
99686           };
99687           staticAccessors.serialVersionUID.get = function () { return -551033529766975875 };
99688
99689           Object.defineProperties( MultiPolygon, staticAccessors );
99690
99691           return MultiPolygon;
99692         }(GeometryCollection));
99693
99694         var GeometryEditor = function GeometryEditor (factory) {
99695           this._factory = factory || null;
99696           this._isUserDataCopied = false;
99697         };
99698
99699         var staticAccessors$16 = { NoOpGeometryOperation: { configurable: true },CoordinateOperation: { configurable: true },CoordinateSequenceOperation: { configurable: true } };
99700         GeometryEditor.prototype.setCopyUserData = function setCopyUserData (isUserDataCopied) {
99701           this._isUserDataCopied = isUserDataCopied;
99702         };
99703         GeometryEditor.prototype.edit = function edit (geometry, operation) {
99704           if (geometry === null) { return null }
99705           var result = this.editInternal(geometry, operation);
99706           if (this._isUserDataCopied) {
99707             result.setUserData(geometry.getUserData());
99708           }
99709           return result
99710         };
99711         GeometryEditor.prototype.editInternal = function editInternal (geometry, operation) {
99712           if (this._factory === null) { this._factory = geometry.getFactory(); }
99713           if (geometry instanceof GeometryCollection) {
99714             return this.editGeometryCollection(geometry, operation)
99715           }
99716           if (geometry instanceof Polygon) {
99717             return this.editPolygon(geometry, operation)
99718           }
99719           if (geometry instanceof Point$1) {
99720             return operation.edit(geometry, this._factory)
99721           }
99722           if (geometry instanceof LineString) {
99723             return operation.edit(geometry, this._factory)
99724           }
99725           Assert.shouldNeverReachHere('Unsupported Geometry class: ' + geometry.getClass().getName());
99726           return null
99727         };
99728         GeometryEditor.prototype.editGeometryCollection = function editGeometryCollection (collection, operation) {
99729             var this$1 = this;
99730
99731           var collectionForType = operation.edit(collection, this._factory);
99732           var geometries = new ArrayList();
99733           for (var i = 0; i < collectionForType.getNumGeometries(); i++) {
99734             var geometry = this$1.edit(collectionForType.getGeometryN(i), operation);
99735             if (geometry === null || geometry.isEmpty()) {
99736               continue
99737             }
99738             geometries.add(geometry);
99739           }
99740           if (collectionForType.getClass() === MultiPoint) {
99741             return this._factory.createMultiPoint(geometries.toArray([]))
99742           }
99743           if (collectionForType.getClass() === MultiLineString) {
99744             return this._factory.createMultiLineString(geometries.toArray([]))
99745           }
99746           if (collectionForType.getClass() === MultiPolygon) {
99747             return this._factory.createMultiPolygon(geometries.toArray([]))
99748           }
99749           return this._factory.createGeometryCollection(geometries.toArray([]))
99750         };
99751         GeometryEditor.prototype.editPolygon = function editPolygon (polygon, operation) {
99752             var this$1 = this;
99753
99754           var newPolygon = operation.edit(polygon, this._factory);
99755           if (newPolygon === null) { newPolygon = this._factory.createPolygon(null); }
99756           if (newPolygon.isEmpty()) {
99757             return newPolygon
99758           }
99759           var shell = this.edit(newPolygon.getExteriorRing(), operation);
99760           if (shell === null || shell.isEmpty()) {
99761             return this._factory.createPolygon()
99762           }
99763           var holes = new ArrayList();
99764           for (var i = 0; i < newPolygon.getNumInteriorRing(); i++) {
99765             var hole = this$1.edit(newPolygon.getInteriorRingN(i), operation);
99766             if (hole === null || hole.isEmpty()) {
99767               continue
99768             }
99769             holes.add(hole);
99770           }
99771           return this._factory.createPolygon(shell, holes.toArray([]))
99772         };
99773         GeometryEditor.prototype.interfaces_ = function interfaces_ () {
99774           return []
99775         };
99776         GeometryEditor.prototype.getClass = function getClass () {
99777           return GeometryEditor
99778         };
99779         GeometryEditor.GeometryEditorOperation = function GeometryEditorOperation () {};
99780         staticAccessors$16.NoOpGeometryOperation.get = function () { return NoOpGeometryOperation };
99781         staticAccessors$16.CoordinateOperation.get = function () { return CoordinateOperation };
99782         staticAccessors$16.CoordinateSequenceOperation.get = function () { return CoordinateSequenceOperation };
99783
99784         Object.defineProperties( GeometryEditor, staticAccessors$16 );
99785
99786         var NoOpGeometryOperation = function NoOpGeometryOperation () {};
99787
99788         NoOpGeometryOperation.prototype.edit = function edit (geometry, factory) {
99789           return geometry
99790         };
99791         NoOpGeometryOperation.prototype.interfaces_ = function interfaces_ () {
99792           return [GeometryEditor.GeometryEditorOperation]
99793         };
99794         NoOpGeometryOperation.prototype.getClass = function getClass () {
99795           return NoOpGeometryOperation
99796         };
99797
99798         var CoordinateOperation = function CoordinateOperation () {};
99799
99800         CoordinateOperation.prototype.edit = function edit (geometry, factory) {
99801           var coords = this.editCoordinates(geometry.getCoordinates(), geometry);
99802           if (coords === null) { return geometry }
99803           if (geometry instanceof LinearRing) {
99804             return factory.createLinearRing(coords)
99805           }
99806           if (geometry instanceof LineString) {
99807             return factory.createLineString(coords)
99808           }
99809           if (geometry instanceof Point$1) {
99810             if (coords.length > 0) {
99811               return factory.createPoint(coords[0])
99812             } else {
99813               return factory.createPoint()
99814             }
99815           }
99816           return geometry
99817         };
99818         CoordinateOperation.prototype.interfaces_ = function interfaces_ () {
99819           return [GeometryEditor.GeometryEditorOperation]
99820         };
99821         CoordinateOperation.prototype.getClass = function getClass () {
99822           return CoordinateOperation
99823         };
99824
99825         var CoordinateSequenceOperation = function CoordinateSequenceOperation () {};
99826
99827         CoordinateSequenceOperation.prototype.edit = function edit (geometry, factory) {
99828           if (geometry instanceof LinearRing) {
99829             return factory.createLinearRing(this.edit(geometry.getCoordinateSequence(), geometry))
99830           }
99831           if (geometry instanceof LineString) {
99832             return factory.createLineString(this.edit(geometry.getCoordinateSequence(), geometry))
99833           }
99834           if (geometry instanceof Point$1) {
99835             return factory.createPoint(this.edit(geometry.getCoordinateSequence(), geometry))
99836           }
99837           return geometry
99838         };
99839         CoordinateSequenceOperation.prototype.interfaces_ = function interfaces_ () {
99840           return [GeometryEditor.GeometryEditorOperation]
99841         };
99842         CoordinateSequenceOperation.prototype.getClass = function getClass () {
99843           return CoordinateSequenceOperation
99844         };
99845
99846         var CoordinateArraySequence = function CoordinateArraySequence () {
99847           var this$1 = this;
99848
99849           this._dimension = 3;
99850           this._coordinates = null;
99851           if (arguments.length === 1) {
99852             if (arguments[0] instanceof Array) {
99853               this._coordinates = arguments[0];
99854               this._dimension = 3;
99855             } else if (Number.isInteger(arguments[0])) {
99856               var size = arguments[0];
99857               this._coordinates = new Array(size).fill(null);
99858               for (var i = 0; i < size; i++) {
99859                 this$1._coordinates[i] = new Coordinate();
99860               }
99861             } else if (hasInterface(arguments[0], CoordinateSequence)) {
99862               var coordSeq = arguments[0];
99863               if (coordSeq === null) {
99864                 this._coordinates = new Array(0).fill(null);
99865                 return null
99866               }
99867               this._dimension = coordSeq.getDimension();
99868               this._coordinates = new Array(coordSeq.size()).fill(null);
99869               for (var i$1 = 0; i$1 < this._coordinates.length; i$1++) {
99870                 this$1._coordinates[i$1] = coordSeq.getCoordinateCopy(i$1);
99871               }
99872             }
99873           } else if (arguments.length === 2) {
99874             if (arguments[0] instanceof Array && Number.isInteger(arguments[1])) {
99875               var coordinates = arguments[0];
99876               var dimension = arguments[1];
99877               this._coordinates = coordinates;
99878               this._dimension = dimension;
99879               if (coordinates === null) { this._coordinates = new Array(0).fill(null); }
99880             } else if (Number.isInteger(arguments[0]) && Number.isInteger(arguments[1])) {
99881               var size$1 = arguments[0];
99882               var dimension$1 = arguments[1];
99883               this._coordinates = new Array(size$1).fill(null);
99884               this._dimension = dimension$1;
99885               for (var i$2 = 0; i$2 < size$1; i$2++) {
99886                 this$1._coordinates[i$2] = new Coordinate();
99887               }
99888             }
99889           }
99890         };
99891
99892         var staticAccessors$18 = { serialVersionUID: { configurable: true } };
99893         CoordinateArraySequence.prototype.setOrdinate = function setOrdinate (index, ordinateIndex, value) {
99894           switch (ordinateIndex) {
99895             case CoordinateSequence.X:
99896               this._coordinates[index].x = value;
99897               break
99898             case CoordinateSequence.Y:
99899               this._coordinates[index].y = value;
99900               break
99901             case CoordinateSequence.Z:
99902               this._coordinates[index].z = value;
99903               break
99904             default:
99905               throw new IllegalArgumentException('invalid ordinateIndex')
99906           }
99907         };
99908         CoordinateArraySequence.prototype.size = function size () {
99909           return this._coordinates.length
99910         };
99911         CoordinateArraySequence.prototype.getOrdinate = function getOrdinate (index, ordinateIndex) {
99912           switch (ordinateIndex) {
99913             case CoordinateSequence.X:
99914               return this._coordinates[index].x
99915             case CoordinateSequence.Y:
99916               return this._coordinates[index].y
99917             case CoordinateSequence.Z:
99918               return this._coordinates[index].z
99919           }
99920           return Double.NaN
99921         };
99922         CoordinateArraySequence.prototype.getCoordinate = function getCoordinate () {
99923           if (arguments.length === 1) {
99924             var i = arguments[0];
99925             return this._coordinates[i]
99926           } else if (arguments.length === 2) {
99927             var index = arguments[0];
99928             var coord = arguments[1];
99929             coord.x = this._coordinates[index].x;
99930             coord.y = this._coordinates[index].y;
99931             coord.z = this._coordinates[index].z;
99932           }
99933         };
99934         CoordinateArraySequence.prototype.getCoordinateCopy = function getCoordinateCopy (i) {
99935           return new Coordinate(this._coordinates[i])
99936         };
99937         CoordinateArraySequence.prototype.getDimension = function getDimension () {
99938           return this._dimension
99939         };
99940         CoordinateArraySequence.prototype.getX = function getX (index) {
99941           return this._coordinates[index].x
99942         };
99943         CoordinateArraySequence.prototype.clone = function clone () {
99944             var this$1 = this;
99945
99946           var cloneCoordinates = new Array(this.size()).fill(null);
99947           for (var i = 0; i < this._coordinates.length; i++) {
99948             cloneCoordinates[i] = this$1._coordinates[i].clone();
99949           }
99950           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99951         };
99952         CoordinateArraySequence.prototype.expandEnvelope = function expandEnvelope (env) {
99953             var this$1 = this;
99954
99955           for (var i = 0; i < this._coordinates.length; i++) {
99956             env.expandToInclude(this$1._coordinates[i]);
99957           }
99958           return env
99959         };
99960         CoordinateArraySequence.prototype.copy = function copy () {
99961             var this$1 = this;
99962
99963           var cloneCoordinates = new Array(this.size()).fill(null);
99964           for (var i = 0; i < this._coordinates.length; i++) {
99965             cloneCoordinates[i] = this$1._coordinates[i].copy();
99966           }
99967           return new CoordinateArraySequence(cloneCoordinates, this._dimension)
99968         };
99969         CoordinateArraySequence.prototype.toString = function toString () {
99970             var this$1 = this;
99971
99972           if (this._coordinates.length > 0) {
99973             var strBuf = new StringBuffer(17 * this._coordinates.length);
99974             strBuf.append('(');
99975             strBuf.append(this._coordinates[0]);
99976             for (var i = 1; i < this._coordinates.length; i++) {
99977               strBuf.append(', ');
99978               strBuf.append(this$1._coordinates[i]);
99979             }
99980             strBuf.append(')');
99981             return strBuf.toString()
99982           } else {
99983             return '()'
99984           }
99985         };
99986         CoordinateArraySequence.prototype.getY = function getY (index) {
99987           return this._coordinates[index].y
99988         };
99989         CoordinateArraySequence.prototype.toCoordinateArray = function toCoordinateArray () {
99990           return this._coordinates
99991         };
99992         CoordinateArraySequence.prototype.interfaces_ = function interfaces_ () {
99993           return [CoordinateSequence, Serializable]
99994         };
99995         CoordinateArraySequence.prototype.getClass = function getClass () {
99996           return CoordinateArraySequence
99997         };
99998         staticAccessors$18.serialVersionUID.get = function () { return -915438501601840650 };
99999
100000         Object.defineProperties( CoordinateArraySequence, staticAccessors$18 );
100001
100002         var CoordinateArraySequenceFactory = function CoordinateArraySequenceFactory () {};
100003
100004         var staticAccessors$17 = { serialVersionUID: { configurable: true },instanceObject: { configurable: true } };
100005
100006         CoordinateArraySequenceFactory.prototype.readResolve = function readResolve () {
100007           return CoordinateArraySequenceFactory.instance()
100008         };
100009         CoordinateArraySequenceFactory.prototype.create = function create () {
100010           if (arguments.length === 1) {
100011             if (arguments[0] instanceof Array) {
100012               var coordinates = arguments[0];
100013               return new CoordinateArraySequence(coordinates)
100014             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100015               var coordSeq = arguments[0];
100016               return new CoordinateArraySequence(coordSeq)
100017             }
100018           } else if (arguments.length === 2) {
100019             var size = arguments[0];
100020             var dimension = arguments[1];
100021             if (dimension > 3) { dimension = 3; }
100022             if (dimension < 2) { return new CoordinateArraySequence(size) }
100023             return new CoordinateArraySequence(size, dimension)
100024           }
100025         };
100026         CoordinateArraySequenceFactory.prototype.interfaces_ = function interfaces_ () {
100027           return [CoordinateSequenceFactory, Serializable]
100028         };
100029         CoordinateArraySequenceFactory.prototype.getClass = function getClass () {
100030           return CoordinateArraySequenceFactory
100031         };
100032         CoordinateArraySequenceFactory.instance = function instance () {
100033           return CoordinateArraySequenceFactory.instanceObject
100034         };
100035
100036         staticAccessors$17.serialVersionUID.get = function () { return -4099577099607551657 };
100037         staticAccessors$17.instanceObject.get = function () { return new CoordinateArraySequenceFactory() };
100038
100039         Object.defineProperties( CoordinateArraySequenceFactory, staticAccessors$17 );
100040
100041         /**
100042          * @see http://download.oracle.com/javase/6/docs/api/java/util/HashMap.html
100043          *
100044          * @extends {javascript.util.Map}
100045          * @constructor
100046          * @private
100047          */
100048         var HashMap = (function (MapInterface) {
100049           function HashMap () {
100050             MapInterface.call(this);
100051             this.map_ = new Map();
100052           }
100053
100054           if ( MapInterface ) { HashMap.__proto__ = MapInterface; }
100055           HashMap.prototype = Object.create( MapInterface && MapInterface.prototype );
100056           HashMap.prototype.constructor = HashMap;
100057           /**
100058            * @override
100059            */
100060           HashMap.prototype.get = function get (key) {
100061             return this.map_.get(key) || null
100062           };
100063
100064           /**
100065            * @override
100066            */
100067           HashMap.prototype.put = function put (key, value) {
100068             this.map_.set(key, value);
100069             return value
100070           };
100071
100072           /**
100073            * @override
100074            */
100075           HashMap.prototype.values = function values () {
100076             var arrayList = new ArrayList();
100077             var it = this.map_.values();
100078             var o = it.next();
100079             while (!o.done) {
100080               arrayList.add(o.value);
100081               o = it.next();
100082             }
100083             return arrayList
100084           };
100085
100086           /**
100087            * @override
100088            */
100089           HashMap.prototype.entrySet = function entrySet () {
100090             var hashSet = new HashSet();
100091             this.map_.entries().forEach(function (entry) { return hashSet.add(entry); });
100092             return hashSet
100093           };
100094
100095           /**
100096            * @override
100097            */
100098           HashMap.prototype.size = function size () {
100099             return this.map_.size()
100100           };
100101
100102           return HashMap;
100103         }(Map$1$1));
100104
100105         var PrecisionModel = function PrecisionModel () {
100106           this._modelType = null;
100107           this._scale = null;
100108           if (arguments.length === 0) {
100109             this._modelType = PrecisionModel.FLOATING;
100110           } else if (arguments.length === 1) {
100111             if (arguments[0] instanceof Type$2) {
100112               var modelType = arguments[0];
100113               this._modelType = modelType;
100114               if (modelType === PrecisionModel.FIXED) {
100115                 this.setScale(1.0);
100116               }
100117             } else if (typeof arguments[0] === 'number') {
100118               var scale = arguments[0];
100119               this._modelType = PrecisionModel.FIXED;
100120               this.setScale(scale);
100121             } else if (arguments[0] instanceof PrecisionModel) {
100122               var pm = arguments[0];
100123               this._modelType = pm._modelType;
100124               this._scale = pm._scale;
100125             }
100126           }
100127         };
100128
100129         var staticAccessors$19 = { serialVersionUID: { configurable: true },maximumPreciseValue: { configurable: true } };
100130         PrecisionModel.prototype.equals = function equals (other) {
100131           if (!(other instanceof PrecisionModel)) {
100132             return false
100133           }
100134           var otherPrecisionModel = other;
100135           return this._modelType === otherPrecisionModel._modelType && this._scale === otherPrecisionModel._scale
100136         };
100137         PrecisionModel.prototype.compareTo = function compareTo (o) {
100138           var other = o;
100139           var sigDigits = this.getMaximumSignificantDigits();
100140           var otherSigDigits = other.getMaximumSignificantDigits();
100141           return new Integer(sigDigits).compareTo(new Integer(otherSigDigits))
100142         };
100143         PrecisionModel.prototype.getScale = function getScale () {
100144           return this._scale
100145         };
100146         PrecisionModel.prototype.isFloating = function isFloating () {
100147           return this._modelType === PrecisionModel.FLOATING || this._modelType === PrecisionModel.FLOATING_SINGLE
100148         };
100149         PrecisionModel.prototype.getType = function getType () {
100150           return this._modelType
100151         };
100152         PrecisionModel.prototype.toString = function toString () {
100153           var description = 'UNKNOWN';
100154           if (this._modelType === PrecisionModel.FLOATING) {
100155             description = 'Floating';
100156           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100157             description = 'Floating-Single';
100158           } else if (this._modelType === PrecisionModel.FIXED) {
100159             description = 'Fixed (Scale=' + this.getScale() + ')';
100160           }
100161           return description
100162         };
100163         PrecisionModel.prototype.makePrecise = function makePrecise () {
100164           if (typeof arguments[0] === 'number') {
100165             var val = arguments[0];
100166             if (Double.isNaN(val)) { return val }
100167             if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100168               var floatSingleVal = val;
100169               return floatSingleVal
100170             }
100171             if (this._modelType === PrecisionModel.FIXED) {
100172               return Math.round(val * this._scale) / this._scale
100173             }
100174             return val
100175           } else if (arguments[0] instanceof Coordinate) {
100176             var coord = arguments[0];
100177             if (this._modelType === PrecisionModel.FLOATING) { return null }
100178             coord.x = this.makePrecise(coord.x);
100179             coord.y = this.makePrecise(coord.y);
100180           }
100181         };
100182         PrecisionModel.prototype.getMaximumSignificantDigits = function getMaximumSignificantDigits () {
100183           var maxSigDigits = 16;
100184           if (this._modelType === PrecisionModel.FLOATING) {
100185             maxSigDigits = 16;
100186           } else if (this._modelType === PrecisionModel.FLOATING_SINGLE) {
100187             maxSigDigits = 6;
100188           } else if (this._modelType === PrecisionModel.FIXED) {
100189             maxSigDigits = 1 + Math.trunc(Math.ceil(Math.log(this.getScale()) / Math.log(10)));
100190           }
100191           return maxSigDigits
100192         };
100193         PrecisionModel.prototype.setScale = function setScale (scale) {
100194           this._scale = Math.abs(scale);
100195         };
100196         PrecisionModel.prototype.interfaces_ = function interfaces_ () {
100197           return [Serializable, Comparable]
100198         };
100199         PrecisionModel.prototype.getClass = function getClass () {
100200           return PrecisionModel
100201         };
100202         PrecisionModel.mostPrecise = function mostPrecise (pm1, pm2) {
100203           if (pm1.compareTo(pm2) >= 0) { return pm1 }
100204           return pm2
100205         };
100206         staticAccessors$19.serialVersionUID.get = function () { return 7777263578777803835 };
100207         staticAccessors$19.maximumPreciseValue.get = function () { return 9007199254740992.0 };
100208
100209         Object.defineProperties( PrecisionModel, staticAccessors$19 );
100210
100211         var Type$2 = function Type (name) {
100212           this._name = name || null;
100213           Type.nameToTypeMap.put(name, this);
100214         };
100215
100216         var staticAccessors$1$1 = { serialVersionUID: { configurable: true },nameToTypeMap: { configurable: true } };
100217         Type$2.prototype.readResolve = function readResolve () {
100218           return Type$2.nameToTypeMap.get(this._name)
100219         };
100220         Type$2.prototype.toString = function toString () {
100221           return this._name
100222         };
100223         Type$2.prototype.interfaces_ = function interfaces_ () {
100224           return [Serializable]
100225         };
100226         Type$2.prototype.getClass = function getClass () {
100227           return Type$2
100228         };
100229         staticAccessors$1$1.serialVersionUID.get = function () { return -5528602631731589822 };
100230         staticAccessors$1$1.nameToTypeMap.get = function () { return new HashMap() };
100231
100232         Object.defineProperties( Type$2, staticAccessors$1$1 );
100233
100234         PrecisionModel.Type = Type$2;
100235         PrecisionModel.FIXED = new Type$2('FIXED');
100236         PrecisionModel.FLOATING = new Type$2('FLOATING');
100237         PrecisionModel.FLOATING_SINGLE = new Type$2('FLOATING SINGLE');
100238
100239         var GeometryFactory = function GeometryFactory () {
100240           this._precisionModel = new PrecisionModel();
100241           this._SRID = 0;
100242           this._coordinateSequenceFactory = GeometryFactory.getDefaultCoordinateSequenceFactory();
100243
100244           if (arguments.length === 0) ; else if (arguments.length === 1) {
100245             if (hasInterface(arguments[0], CoordinateSequenceFactory)) {
100246               this._coordinateSequenceFactory = arguments[0];
100247             } else if (arguments[0] instanceof PrecisionModel) {
100248               this._precisionModel = arguments[0];
100249             }
100250           } else if (arguments.length === 2) {
100251             this._precisionModel = arguments[0];
100252             this._SRID = arguments[1];
100253           } else if (arguments.length === 3) {
100254             this._precisionModel = arguments[0];
100255             this._SRID = arguments[1];
100256             this._coordinateSequenceFactory = arguments[2];
100257           }
100258         };
100259
100260         var staticAccessors$2 = { serialVersionUID: { configurable: true } };
100261         GeometryFactory.prototype.toGeometry = function toGeometry (envelope) {
100262           if (envelope.isNull()) {
100263             return this.createPoint(null)
100264           }
100265           if (envelope.getMinX() === envelope.getMaxX() && envelope.getMinY() === envelope.getMaxY()) {
100266             return this.createPoint(new Coordinate(envelope.getMinX(), envelope.getMinY()))
100267           }
100268           if (envelope.getMinX() === envelope.getMaxX() || envelope.getMinY() === envelope.getMaxY()) {
100269             return this.createLineString([new Coordinate(envelope.getMinX(), envelope.getMinY()), new Coordinate(envelope.getMaxX(), envelope.getMaxY())])
100270           }
100271           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)
100272         };
100273         GeometryFactory.prototype.createLineString = function createLineString (coordinates) {
100274           if (!coordinates) { return new LineString(this.getCoordinateSequenceFactory().create([]), this) }
100275           else if (coordinates instanceof Array) { return new LineString(this.getCoordinateSequenceFactory().create(coordinates), this) }
100276           else if (hasInterface(coordinates, CoordinateSequence)) { return new LineString(coordinates, this) }
100277         };
100278         GeometryFactory.prototype.createMultiLineString = function createMultiLineString () {
100279           if (arguments.length === 0) {
100280             return new MultiLineString(null, this)
100281           } else if (arguments.length === 1) {
100282             var lineStrings = arguments[0];
100283             return new MultiLineString(lineStrings, this)
100284           }
100285         };
100286         GeometryFactory.prototype.buildGeometry = function buildGeometry (geomList) {
100287           var geomClass = null;
100288           var isHeterogeneous = false;
100289           var hasGeometryCollection = false;
100290           for (var i = geomList.iterator(); i.hasNext();) {
100291             var geom = i.next();
100292             var partClass = geom.getClass();
100293             if (geomClass === null) {
100294               geomClass = partClass;
100295             }
100296             if (partClass !== geomClass) {
100297               isHeterogeneous = true;
100298             }
100299             if (geom.isGeometryCollectionOrDerived()) { hasGeometryCollection = true; }
100300           }
100301           if (geomClass === null) {
100302             return this.createGeometryCollection()
100303           }
100304           if (isHeterogeneous || hasGeometryCollection) {
100305             return this.createGeometryCollection(GeometryFactory.toGeometryArray(geomList))
100306           }
100307           var geom0 = geomList.iterator().next();
100308           var isCollection = geomList.size() > 1;
100309           if (isCollection) {
100310             if (geom0 instanceof Polygon) {
100311               return this.createMultiPolygon(GeometryFactory.toPolygonArray(geomList))
100312             } else if (geom0 instanceof LineString) {
100313               return this.createMultiLineString(GeometryFactory.toLineStringArray(geomList))
100314             } else if (geom0 instanceof Point$1) {
100315               return this.createMultiPoint(GeometryFactory.toPointArray(geomList))
100316             }
100317             Assert.shouldNeverReachHere('Unhandled class: ' + geom0.getClass().getName());
100318           }
100319           return geom0
100320         };
100321         GeometryFactory.prototype.createMultiPointFromCoords = function createMultiPointFromCoords (coordinates) {
100322           return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100323         };
100324         GeometryFactory.prototype.createPoint = function createPoint () {
100325           if (arguments.length === 0) {
100326             return this.createPoint(this.getCoordinateSequenceFactory().create([]))
100327           } else if (arguments.length === 1) {
100328             if (arguments[0] instanceof Coordinate) {
100329               var coordinate = arguments[0];
100330               return this.createPoint(coordinate !== null ? this.getCoordinateSequenceFactory().create([coordinate]) : null)
100331             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100332               var coordinates = arguments[0];
100333               return new Point$1(coordinates, this)
100334             }
100335           }
100336         };
100337         GeometryFactory.prototype.getCoordinateSequenceFactory = function getCoordinateSequenceFactory () {
100338           return this._coordinateSequenceFactory
100339         };
100340         GeometryFactory.prototype.createPolygon = function createPolygon () {
100341           if (arguments.length === 0) {
100342             return new Polygon(null, null, this)
100343           } else if (arguments.length === 1) {
100344             if (hasInterface(arguments[0], CoordinateSequence)) {
100345               var coordinates = arguments[0];
100346               return this.createPolygon(this.createLinearRing(coordinates))
100347             } else if (arguments[0] instanceof Array) {
100348               var coordinates$1 = arguments[0];
100349               return this.createPolygon(this.createLinearRing(coordinates$1))
100350             } else if (arguments[0] instanceof LinearRing) {
100351               var shell = arguments[0];
100352               return this.createPolygon(shell, null)
100353             }
100354           } else if (arguments.length === 2) {
100355             var shell$1 = arguments[0];
100356             var holes = arguments[1];
100357             return new Polygon(shell$1, holes, this)
100358           }
100359         };
100360         GeometryFactory.prototype.getSRID = function getSRID () {
100361           return this._SRID
100362         };
100363         GeometryFactory.prototype.createGeometryCollection = function createGeometryCollection () {
100364           if (arguments.length === 0) {
100365             return new GeometryCollection(null, this)
100366           } else if (arguments.length === 1) {
100367             var geometries = arguments[0];
100368             return new GeometryCollection(geometries, this)
100369           }
100370         };
100371         GeometryFactory.prototype.createGeometry = function createGeometry (g) {
100372           var editor = new GeometryEditor(this);
100373           return editor.edit(g, {
100374             edit: function () {
100375               if (arguments.length === 2) {
100376                 var coordSeq = arguments[0];
100377                 // const geometry = arguments[1]
100378                 return this._coordinateSequenceFactory.create(coordSeq)
100379               }
100380             }
100381           })
100382         };
100383         GeometryFactory.prototype.getPrecisionModel = function getPrecisionModel () {
100384           return this._precisionModel
100385         };
100386         GeometryFactory.prototype.createLinearRing = function createLinearRing () {
100387           if (arguments.length === 0) {
100388             return this.createLinearRing(this.getCoordinateSequenceFactory().create([]))
100389           } else if (arguments.length === 1) {
100390             if (arguments[0] instanceof Array) {
100391               var coordinates = arguments[0];
100392               return this.createLinearRing(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100393             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100394               var coordinates$1 = arguments[0];
100395               return new LinearRing(coordinates$1, this)
100396             }
100397           }
100398         };
100399         GeometryFactory.prototype.createMultiPolygon = function createMultiPolygon () {
100400           if (arguments.length === 0) {
100401             return new MultiPolygon(null, this)
100402           } else if (arguments.length === 1) {
100403             var polygons = arguments[0];
100404             return new MultiPolygon(polygons, this)
100405           }
100406         };
100407         GeometryFactory.prototype.createMultiPoint = function createMultiPoint () {
100408             var this$1 = this;
100409
100410           if (arguments.length === 0) {
100411             return new MultiPoint(null, this)
100412           } else if (arguments.length === 1) {
100413             if (arguments[0] instanceof Array) {
100414               var point = arguments[0];
100415               return new MultiPoint(point, this)
100416             } else if (arguments[0] instanceof Array) {
100417               var coordinates = arguments[0];
100418               return this.createMultiPoint(coordinates !== null ? this.getCoordinateSequenceFactory().create(coordinates) : null)
100419             } else if (hasInterface(arguments[0], CoordinateSequence)) {
100420               var coordinates$1 = arguments[0];
100421               if (coordinates$1 === null) {
100422                 return this.createMultiPoint(new Array(0).fill(null))
100423               }
100424               var points = new Array(coordinates$1.size()).fill(null);
100425               for (var i = 0; i < coordinates$1.size(); i++) {
100426                 var ptSeq = this$1.getCoordinateSequenceFactory().create(1, coordinates$1.getDimension());
100427                 CoordinateSequences.copy(coordinates$1, i, ptSeq, 0, 1);
100428                 points[i] = this$1.createPoint(ptSeq);
100429               }
100430               return this.createMultiPoint(points)
100431             }
100432           }
100433         };
100434         GeometryFactory.prototype.interfaces_ = function interfaces_ () {
100435           return [Serializable]
100436         };
100437         GeometryFactory.prototype.getClass = function getClass () {
100438           return GeometryFactory
100439         };
100440         GeometryFactory.toMultiPolygonArray = function toMultiPolygonArray (multiPolygons) {
100441           var multiPolygonArray = new Array(multiPolygons.size()).fill(null);
100442           return multiPolygons.toArray(multiPolygonArray)
100443         };
100444         GeometryFactory.toGeometryArray = function toGeometryArray (geometries) {
100445           if (geometries === null) { return null }
100446           var geometryArray = new Array(geometries.size()).fill(null);
100447           return geometries.toArray(geometryArray)
100448         };
100449         GeometryFactory.getDefaultCoordinateSequenceFactory = function getDefaultCoordinateSequenceFactory () {
100450           return CoordinateArraySequenceFactory.instance()
100451         };
100452         GeometryFactory.toMultiLineStringArray = function toMultiLineStringArray (multiLineStrings) {
100453           var multiLineStringArray = new Array(multiLineStrings.size()).fill(null);
100454           return multiLineStrings.toArray(multiLineStringArray)
100455         };
100456         GeometryFactory.toLineStringArray = function toLineStringArray (lineStrings) {
100457           var lineStringArray = new Array(lineStrings.size()).fill(null);
100458           return lineStrings.toArray(lineStringArray)
100459         };
100460         GeometryFactory.toMultiPointArray = function toMultiPointArray (multiPoints) {
100461           var multiPointArray = new Array(multiPoints.size()).fill(null);
100462           return multiPoints.toArray(multiPointArray)
100463         };
100464         GeometryFactory.toLinearRingArray = function toLinearRingArray (linearRings) {
100465           var linearRingArray = new Array(linearRings.size()).fill(null);
100466           return linearRings.toArray(linearRingArray)
100467         };
100468         GeometryFactory.toPointArray = function toPointArray (points) {
100469           var pointArray = new Array(points.size()).fill(null);
100470           return points.toArray(pointArray)
100471         };
100472         GeometryFactory.toPolygonArray = function toPolygonArray (polygons) {
100473           var polygonArray = new Array(polygons.size()).fill(null);
100474           return polygons.toArray(polygonArray)
100475         };
100476         GeometryFactory.createPointFromInternalCoord = function createPointFromInternalCoord (coord, exemplar) {
100477           exemplar.getPrecisionModel().makePrecise(coord);
100478           return exemplar.getFactory().createPoint(coord)
100479         };
100480         staticAccessors$2.serialVersionUID.get = function () { return -6820524753094095635 };
100481
100482         Object.defineProperties( GeometryFactory, staticAccessors$2 );
100483
100484         var geometryTypes = ['Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon'];
100485
100486         /**
100487          * Class for reading and writing Well-Known Text.Create a new parser for GeoJSON
100488          * NOTE: Adapted from OpenLayers 2.11 implementation.
100489          */
100490
100491         /**
100492          * Create a new parser for GeoJSON
100493          *
100494          * @param {GeometryFactory} geometryFactory
100495          * @return An instance of GeoJsonParser.
100496          * @constructor
100497          * @private
100498          */
100499         var GeoJSONParser = function GeoJSONParser (geometryFactory) {
100500           this.geometryFactory = geometryFactory || new GeometryFactory();
100501         };
100502         /**
100503          * Deserialize a GeoJSON object and return the Geometry or Feature(Collection) with JSTS Geometries
100504          *
100505          * @param {}
100506          *        A GeoJSON object.
100507          * @return {} A Geometry instance or object representing a Feature(Collection) with Geometry instances.
100508          * @private
100509          */
100510         GeoJSONParser.prototype.read = function read (json) {
100511           var obj;
100512           if (typeof json === 'string') {
100513             obj = JSON.parse(json);
100514           } else {
100515             obj = json;
100516           }
100517
100518           var type = obj.type;
100519
100520           if (!parse$2[type]) {
100521             throw new Error('Unknown GeoJSON type: ' + obj.type)
100522           }
100523
100524           if (geometryTypes.indexOf(type) !== -1) {
100525             return parse$2[type].apply(this, [obj.coordinates])
100526           } else if (type === 'GeometryCollection') {
100527             return parse$2[type].apply(this, [obj.geometries])
100528           }
100529
100530           // feature or feature collection
100531           return parse$2[type].apply(this, [obj])
100532         };
100533
100534         /**
100535          * Serialize a Geometry object into GeoJSON
100536          *
100537          * @param {Geometry}
100538          *        geometry A Geometry or array of Geometries.
100539          * @return {Object} A GeoJSON object represting the input Geometry/Geometries.
100540          * @private
100541          */
100542         GeoJSONParser.prototype.write = function write (geometry) {
100543           var type = geometry.getGeometryType();
100544
100545           if (!extract[type]) {
100546             throw new Error('Geometry is not supported')
100547           }
100548
100549           return extract[type].apply(this, [geometry])
100550         };
100551
100552         var parse$2 = {
100553           /**
100554            * Parse a GeoJSON Feature object
100555            *
100556            * @param {Object}
100557            *          obj Object to parse.
100558            *
100559            * @return {Object} Feature with geometry/bbox converted to JSTS Geometries.
100560            */
100561           Feature: function (obj) {
100562             var feature = {};
100563
100564             // copy features
100565             for (var key in obj) {
100566               feature[key] = obj[key];
100567             }
100568
100569             // parse geometry
100570             if (obj.geometry) {
100571               var type = obj.geometry.type;
100572               if (!parse$2[type]) {
100573                 throw new Error('Unknown GeoJSON type: ' + obj.type)
100574               }
100575               feature.geometry = this.read(obj.geometry);
100576             }
100577
100578             // bbox
100579             if (obj.bbox) {
100580               feature.bbox = parse$2.bbox.apply(this, [obj.bbox]);
100581             }
100582
100583             return feature
100584           },
100585
100586           /**
100587            * Parse a GeoJSON FeatureCollection object
100588            *
100589            * @param {Object}
100590            *          obj Object to parse.
100591            *
100592            * @return {Object} FeatureCollection with geometry/bbox converted to JSTS Geometries.
100593            */
100594           FeatureCollection: function (obj) {
100595             var this$1 = this;
100596
100597             var featureCollection = {};
100598
100599             if (obj.features) {
100600               featureCollection.features = [];
100601
100602               for (var i = 0; i < obj.features.length; ++i) {
100603                 featureCollection.features.push(this$1.read(obj.features[i]));
100604               }
100605             }
100606
100607             if (obj.bbox) {
100608               featureCollection.bbox = this.parse.bbox.apply(this, [obj.bbox]);
100609             }
100610
100611             return featureCollection
100612           },
100613
100614           /**
100615            * Convert the ordinates in an array to an array of Coordinates
100616            *
100617            * @param {Array}
100618            *          array Array with {Number}s.
100619            *
100620            * @return {Array} Array with Coordinates.
100621            */
100622           coordinates: function (array) {
100623             var coordinates = [];
100624             for (var i = 0; i < array.length; ++i) {
100625               var sub = array[i];
100626               coordinates.push(new Coordinate(sub[0], sub[1]));
100627             }
100628             return coordinates
100629           },
100630
100631           /**
100632            * Convert the bbox to a LinearRing
100633            *
100634            * @param {Array}
100635            *          array Array with [xMin, yMin, xMax, yMax].
100636            *
100637            * @return {Array} Array with Coordinates.
100638            */
100639           bbox: function (array) {
100640             return this.geometryFactory.createLinearRing([
100641               new Coordinate(array[0], array[1]),
100642               new Coordinate(array[2], array[1]),
100643               new Coordinate(array[2], array[3]),
100644               new Coordinate(array[0], array[3]),
100645               new Coordinate(array[0], array[1])
100646             ])
100647           },
100648
100649           /**
100650            * Convert an Array with ordinates to a Point
100651            *
100652            * @param {Array}
100653            *          array Array with ordinates.
100654            *
100655            * @return {Point} Point.
100656            */
100657           Point: function (array) {
100658             var coordinate = new Coordinate(array[0], array[1]);
100659             return this.geometryFactory.createPoint(coordinate)
100660           },
100661
100662           /**
100663            * Convert an Array with coordinates to a MultiPoint
100664            *
100665            * @param {Array}
100666            *          array Array with coordinates.
100667            *
100668            * @return {MultiPoint} MultiPoint.
100669            */
100670           MultiPoint: function (array) {
100671             var this$1 = this;
100672
100673             var points = [];
100674             for (var i = 0; i < array.length; ++i) {
100675               points.push(parse$2.Point.apply(this$1, [array[i]]));
100676             }
100677             return this.geometryFactory.createMultiPoint(points)
100678           },
100679
100680           /**
100681            * Convert an Array with coordinates to a LineString
100682            *
100683            * @param {Array}
100684            *          array Array with coordinates.
100685            *
100686            * @return {LineString} LineString.
100687            */
100688           LineString: function (array) {
100689             var coordinates = parse$2.coordinates.apply(this, [array]);
100690             return this.geometryFactory.createLineString(coordinates)
100691           },
100692
100693           /**
100694            * Convert an Array with coordinates to a MultiLineString
100695            *
100696            * @param {Array}
100697            *          array Array with coordinates.
100698            *
100699            * @return {MultiLineString} MultiLineString.
100700            */
100701           MultiLineString: function (array) {
100702             var this$1 = this;
100703
100704             var lineStrings = [];
100705             for (var i = 0; i < array.length; ++i) {
100706               lineStrings.push(parse$2.LineString.apply(this$1, [array[i]]));
100707             }
100708             return this.geometryFactory.createMultiLineString(lineStrings)
100709           },
100710
100711           /**
100712            * Convert an Array to a Polygon
100713            *
100714            * @param {Array}
100715            *          array Array with shell and holes.
100716            *
100717            * @return {Polygon} Polygon.
100718            */
100719           Polygon: function (array) {
100720             var this$1 = this;
100721
100722             var shellCoordinates = parse$2.coordinates.apply(this, [array[0]]);
100723             var shell = this.geometryFactory.createLinearRing(shellCoordinates);
100724             var holes = [];
100725             for (var i = 1; i < array.length; ++i) {
100726               var hole = array[i];
100727               var coordinates = parse$2.coordinates.apply(this$1, [hole]);
100728               var linearRing = this$1.geometryFactory.createLinearRing(coordinates);
100729               holes.push(linearRing);
100730             }
100731             return this.geometryFactory.createPolygon(shell, holes)
100732           },
100733
100734           /**
100735            * Convert an Array to a MultiPolygon
100736            *
100737            * @param {Array}
100738            *          array Array of arrays with shell and rings.
100739            *
100740            * @return {MultiPolygon} MultiPolygon.
100741            */
100742           MultiPolygon: function (array) {
100743             var this$1 = this;
100744
100745             var polygons = [];
100746             for (var i = 0; i < array.length; ++i) {
100747               var polygon = array[i];
100748               polygons.push(parse$2.Polygon.apply(this$1, [polygon]));
100749             }
100750             return this.geometryFactory.createMultiPolygon(polygons)
100751           },
100752
100753           /**
100754            * Convert an Array to a GeometryCollection
100755            *
100756            * @param {Array}
100757            *          array Array of GeoJSON geometries.
100758            *
100759            * @return {GeometryCollection} GeometryCollection.
100760            */
100761           GeometryCollection: function (array) {
100762             var this$1 = this;
100763
100764             var geometries = [];
100765             for (var i = 0; i < array.length; ++i) {
100766               var geometry = array[i];
100767               geometries.push(this$1.read(geometry));
100768             }
100769             return this.geometryFactory.createGeometryCollection(geometries)
100770           }
100771         };
100772
100773         var extract = {
100774           /**
100775            * Convert a Coordinate to an Array
100776            *
100777            * @param {Coordinate}
100778            *          coordinate Coordinate to convert.
100779            *
100780            * @return {Array} Array of ordinates.
100781            */
100782           coordinate: function (coordinate) {
100783             return [coordinate.x, coordinate.y]
100784           },
100785
100786           /**
100787            * Convert a Point to a GeoJSON object
100788            *
100789            * @param {Point}
100790            *          point Point to convert.
100791            *
100792            * @return {Array} Array of 2 ordinates (paired to a coordinate).
100793            */
100794           Point: function (point) {
100795             var array = extract.coordinate.apply(this, [point.getCoordinate()]);
100796             return {
100797               type: 'Point',
100798               coordinates: array
100799             }
100800           },
100801
100802           /**
100803            * Convert a MultiPoint to a GeoJSON object
100804            *
100805            * @param {MultiPoint}
100806            *          multipoint MultiPoint to convert.
100807            *
100808            * @return {Array} Array of coordinates.
100809            */
100810           MultiPoint: function (multipoint) {
100811             var this$1 = this;
100812
100813             var array = [];
100814             for (var i = 0; i < multipoint._geometries.length; ++i) {
100815               var point = multipoint._geometries[i];
100816               var geoJson = extract.Point.apply(this$1, [point]);
100817               array.push(geoJson.coordinates);
100818             }
100819             return {
100820               type: 'MultiPoint',
100821               coordinates: array
100822             }
100823           },
100824
100825           /**
100826            * Convert a LineString to a GeoJSON object
100827            *
100828            * @param {LineString}
100829            *          linestring LineString to convert.
100830            *
100831            * @return {Array} Array of coordinates.
100832            */
100833           LineString: function (linestring) {
100834             var this$1 = this;
100835
100836             var array = [];
100837             var coordinates = linestring.getCoordinates();
100838             for (var i = 0; i < coordinates.length; ++i) {
100839               var coordinate = coordinates[i];
100840               array.push(extract.coordinate.apply(this$1, [coordinate]));
100841             }
100842             return {
100843               type: 'LineString',
100844               coordinates: array
100845             }
100846           },
100847
100848           /**
100849            * Convert a MultiLineString to a GeoJSON object
100850            *
100851            * @param {MultiLineString}
100852            *          multilinestring MultiLineString to convert.
100853            *
100854            * @return {Array} Array of Array of coordinates.
100855            */
100856           MultiLineString: function (multilinestring) {
100857             var this$1 = this;
100858
100859             var array = [];
100860             for (var i = 0; i < multilinestring._geometries.length; ++i) {
100861               var linestring = multilinestring._geometries[i];
100862               var geoJson = extract.LineString.apply(this$1, [linestring]);
100863               array.push(geoJson.coordinates);
100864             }
100865             return {
100866               type: 'MultiLineString',
100867               coordinates: array
100868             }
100869           },
100870
100871           /**
100872            * Convert a Polygon to a GeoJSON object
100873            *
100874            * @param {Polygon}
100875            *          polygon Polygon to convert.
100876            *
100877            * @return {Array} Array with shell, holes.
100878            */
100879           Polygon: function (polygon) {
100880             var this$1 = this;
100881
100882             var array = [];
100883             var shellGeoJson = extract.LineString.apply(this, [polygon._shell]);
100884             array.push(shellGeoJson.coordinates);
100885             for (var i = 0; i < polygon._holes.length; ++i) {
100886               var hole = polygon._holes[i];
100887               var holeGeoJson = extract.LineString.apply(this$1, [hole]);
100888               array.push(holeGeoJson.coordinates);
100889             }
100890             return {
100891               type: 'Polygon',
100892               coordinates: array
100893             }
100894           },
100895
100896           /**
100897            * Convert a MultiPolygon to a GeoJSON object
100898            *
100899            * @param {MultiPolygon}
100900            *          multipolygon MultiPolygon to convert.
100901            *
100902            * @return {Array} Array of polygons.
100903            */
100904           MultiPolygon: function (multipolygon) {
100905             var this$1 = this;
100906
100907             var array = [];
100908             for (var i = 0; i < multipolygon._geometries.length; ++i) {
100909               var polygon = multipolygon._geometries[i];
100910               var geoJson = extract.Polygon.apply(this$1, [polygon]);
100911               array.push(geoJson.coordinates);
100912             }
100913             return {
100914               type: 'MultiPolygon',
100915               coordinates: array
100916             }
100917           },
100918
100919           /**
100920            * Convert a GeometryCollection to a GeoJSON object
100921            *
100922            * @param {GeometryCollection}
100923            *          collection GeometryCollection to convert.
100924            *
100925            * @return {Array} Array of geometries.
100926            */
100927           GeometryCollection: function (collection) {
100928             var this$1 = this;
100929
100930             var array = [];
100931             for (var i = 0; i < collection._geometries.length; ++i) {
100932               var geometry = collection._geometries[i];
100933               var type = geometry.getGeometryType();
100934               array.push(extract[type].apply(this$1, [geometry]));
100935             }
100936             return {
100937               type: 'GeometryCollection',
100938               geometries: array
100939             }
100940           }
100941         };
100942
100943         /**
100944          * Converts a geometry in GeoJSON to a {@link Geometry}.
100945          */
100946
100947         /**
100948          * A <code>GeoJSONReader</code> is parameterized by a <code>GeometryFactory</code>,
100949          * to allow it to create <code>Geometry</code> objects of the appropriate
100950          * implementation. In particular, the <code>GeometryFactory</code> determines
100951          * the <code>PrecisionModel</code> and <code>SRID</code> that is used.
100952          *
100953          * @param {GeometryFactory} geometryFactory
100954          * @constructor
100955          */
100956         var GeoJSONReader = function GeoJSONReader (geometryFactory) {
100957           this.geometryFactory = geometryFactory || new GeometryFactory();
100958           this.precisionModel = this.geometryFactory.getPrecisionModel();
100959           this.parser = new GeoJSONParser(this.geometryFactory);
100960         };
100961         /**
100962          * Reads a GeoJSON representation of a {@link Geometry}
100963          *
100964          * Will also parse GeoJSON Features/FeatureCollections as custom objects.
100965          *
100966          * @param {Object|String} geoJson a GeoJSON Object or String.
100967          * @return {Geometry|Object} a <code>Geometry or Feature/FeatureCollection representation.</code>
100968          * @memberof GeoJSONReader
100969          */
100970         GeoJSONReader.prototype.read = function read (geoJson) {
100971           var geometry = this.parser.read(geoJson);
100972
100973           if (this.precisionModel.getType() === PrecisionModel.FIXED) {
100974             this.reducePrecision(geometry);
100975           }
100976
100977           return geometry
100978         };
100979
100980         // NOTE: this is a hack
100981         GeoJSONReader.prototype.reducePrecision = function reducePrecision (geometry) {
100982             var this$1 = this;
100983
100984           var i, len;
100985
100986           if (geometry.coordinate) {
100987             this.precisionModel.makePrecise(geometry.coordinate);
100988           } else if (geometry.points) {
100989             for (i = 0, len = geometry.points.length; i < len; i++) {
100990               this$1.precisionModel.makePrecise(geometry.points[i]);
100991             }
100992           } else if (geometry.geometries) {
100993             for (i = 0, len = geometry.geometries.length; i < len; i++) {
100994               this$1.reducePrecision(geometry.geometries[i]);
100995             }
100996           }
100997         };
100998
100999         /**
101000          * @module GeoJSONWriter
101001          */
101002
101003         /**
101004          * Writes the GeoJSON representation of a {@link Geometry}. The
101005          * The GeoJSON format is defined <A
101006          * HREF="http://geojson.org/geojson-spec.html">here</A>.
101007          */
101008
101009         /**
101010          * The <code>GeoJSONWriter</code> outputs coordinates rounded to the precision
101011          * model. Only the maximum number of decimal places necessary to represent the
101012          * ordinates to the required precision will be output.
101013          *
101014          * @param {GeometryFactory} geometryFactory
101015          * @constructor
101016          */
101017         var GeoJSONWriter = function GeoJSONWriter () {
101018           this.parser = new GeoJSONParser(this.geometryFactory);
101019         };
101020         /**
101021          * Converts a <code>Geometry</code> to its GeoJSON representation.
101022          *
101023          * @param {Geometry}
101024          *        geometry a <code>Geometry</code> to process.
101025          * @return {Object} The GeoJSON representation of the Geometry.
101026          * @memberof GeoJSONWriter
101027          */
101028         GeoJSONWriter.prototype.write = function write (geometry) {
101029           return this.parser.write(geometry)
101030         };
101031
101032         /* eslint-disable no-undef */
101033
101034         // io
101035
101036         var Position = function Position () {};
101037
101038         var staticAccessors$20 = { ON: { configurable: true },LEFT: { configurable: true },RIGHT: { configurable: true } };
101039
101040         Position.prototype.interfaces_ = function interfaces_ () {
101041           return []
101042         };
101043         Position.prototype.getClass = function getClass () {
101044           return Position
101045         };
101046         Position.opposite = function opposite (position) {
101047           if (position === Position.LEFT) { return Position.RIGHT }
101048           if (position === Position.RIGHT) { return Position.LEFT }
101049           return position
101050         };
101051         staticAccessors$20.ON.get = function () { return 0 };
101052         staticAccessors$20.LEFT.get = function () { return 1 };
101053         staticAccessors$20.RIGHT.get = function () { return 2 };
101054
101055         Object.defineProperties( Position, staticAccessors$20 );
101056
101057         /**
101058          * @param {string=} message Optional message
101059          * @extends {Error}
101060          * @constructor
101061          * @private
101062          */
101063         function EmptyStackException (message) {
101064           this.message = message || '';
101065         }
101066         EmptyStackException.prototype = new Error();
101067
101068         /**
101069          * @type {string}
101070          */
101071         EmptyStackException.prototype.name = 'EmptyStackException';
101072
101073         /**
101074          * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html
101075          *
101076          * @extends {List}
101077          * @constructor
101078          * @private
101079          */
101080         function Stack () {
101081           /**
101082            * @type {Array}
101083            * @private
101084            */
101085           this.array_ = [];
101086         }
101087         Stack.prototype = new List();
101088
101089         /**
101090          * @override
101091          */
101092         Stack.prototype.add = function (e) {
101093           this.array_.push(e);
101094           return true
101095         };
101096
101097         /**
101098          * @override
101099          */
101100         Stack.prototype.get = function (index) {
101101           if (index < 0 || index >= this.size()) {
101102             throw new Error()
101103           }
101104
101105           return this.array_[index]
101106         };
101107
101108         /**
101109          * Pushes an item onto the top of this stack.
101110          * @param {Object} e
101111          * @return {Object}
101112          */
101113         Stack.prototype.push = function (e) {
101114           this.array_.push(e);
101115           return e
101116         };
101117
101118         /**
101119          * Pushes an item onto the top of this stack.
101120          * @param {Object} e
101121          * @return {Object}
101122          */
101123         Stack.prototype.pop = function (e) {
101124           if (this.array_.length === 0) {
101125             throw new EmptyStackException()
101126           }
101127
101128           return this.array_.pop()
101129         };
101130
101131         /**
101132          * Looks at the object at the top of this stack without removing it from the
101133          * stack.
101134          * @return {Object}
101135          */
101136         Stack.prototype.peek = function () {
101137           if (this.array_.length === 0) {
101138             throw new EmptyStackException()
101139           }
101140
101141           return this.array_[this.array_.length - 1]
101142         };
101143
101144         /**
101145          * Tests if this stack is empty.
101146          * @return {boolean} true if and only if this stack contains no items; false
101147          *         otherwise.
101148          */
101149         Stack.prototype.empty = function () {
101150           if (this.array_.length === 0) {
101151             return true
101152           } else {
101153             return false
101154           }
101155         };
101156
101157         /**
101158          * @return {boolean}
101159          */
101160         Stack.prototype.isEmpty = function () {
101161           return this.empty()
101162         };
101163
101164         /**
101165          * Returns the 1-based position where an object is on this stack. If the object
101166          * o occurs as an item in this stack, this method returns the distance from the
101167          * top of the stack of the occurrence nearest the top of the stack; the topmost
101168          * item on the stack is considered to be at distance 1. The equals method is
101169          * used to compare o to the items in this stack.
101170          *
101171          * NOTE: does not currently actually use equals. (=== is used)
101172          *
101173          * @param {Object} o
101174          * @return {number} the 1-based position from the top of the stack where the
101175          *         object is located; the return value -1 indicates that the object is
101176          *         not on the stack.
101177          */
101178         Stack.prototype.search = function (o) {
101179           return this.array_.indexOf(o)
101180         };
101181
101182         /**
101183          * @return {number}
101184          * @export
101185          */
101186         Stack.prototype.size = function () {
101187           return this.array_.length
101188         };
101189
101190         /**
101191          * @return {Array}
101192          */
101193         Stack.prototype.toArray = function () {
101194           var this$1 = this;
101195
101196           var array = [];
101197
101198           for (var i = 0, len = this.array_.length; i < len; i++) {
101199             array.push(this$1.array_[i]);
101200           }
101201
101202           return array
101203         };
101204
101205         var RightmostEdgeFinder = function RightmostEdgeFinder () {
101206           this._minIndex = -1;
101207           this._minCoord = null;
101208           this._minDe = null;
101209           this._orientedDe = null;
101210         };
101211         RightmostEdgeFinder.prototype.getCoordinate = function getCoordinate () {
101212           return this._minCoord
101213         };
101214         RightmostEdgeFinder.prototype.getRightmostSide = function getRightmostSide (de, index) {
101215           var side = this.getRightmostSideOfSegment(de, index);
101216           if (side < 0) { side = this.getRightmostSideOfSegment(de, index - 1); }
101217           if (side < 0) {
101218             this._minCoord = null;
101219             this.checkForRightmostCoordinate(de);
101220           }
101221           return side
101222         };
101223         RightmostEdgeFinder.prototype.findRightmostEdgeAtVertex = function findRightmostEdgeAtVertex () {
101224           var pts = this._minDe.getEdge().getCoordinates();
101225           Assert.isTrue(this._minIndex > 0 && this._minIndex < pts.length, 'rightmost point expected to be interior vertex of edge');
101226           var pPrev = pts[this._minIndex - 1];
101227           var pNext = pts[this._minIndex + 1];
101228           var orientation = CGAlgorithms.computeOrientation(this._minCoord, pNext, pPrev);
101229           var usePrev = false;
101230           if (pPrev.y < this._minCoord.y && pNext.y < this._minCoord.y && orientation === CGAlgorithms.COUNTERCLOCKWISE) {
101231             usePrev = true;
101232           } else if (pPrev.y > this._minCoord.y && pNext.y > this._minCoord.y && orientation === CGAlgorithms.CLOCKWISE) {
101233             usePrev = true;
101234           }
101235           if (usePrev) {
101236             this._minIndex = this._minIndex - 1;
101237           }
101238         };
101239         RightmostEdgeFinder.prototype.getRightmostSideOfSegment = function getRightmostSideOfSegment (de, i) {
101240           var e = de.getEdge();
101241           var coord = e.getCoordinates();
101242           if (i < 0 || i + 1 >= coord.length) { return -1 }
101243           if (coord[i].y === coord[i + 1].y) { return -1 }
101244           var pos = Position.LEFT;
101245           if (coord[i].y < coord[i + 1].y) { pos = Position.RIGHT; }
101246           return pos
101247         };
101248         RightmostEdgeFinder.prototype.getEdge = function getEdge () {
101249           return this._orientedDe
101250         };
101251         RightmostEdgeFinder.prototype.checkForRightmostCoordinate = function checkForRightmostCoordinate (de) {
101252             var this$1 = this;
101253
101254           var coord = de.getEdge().getCoordinates();
101255           for (var i = 0; i < coord.length - 1; i++) {
101256             if (this$1._minCoord === null || coord[i].x > this$1._minCoord.x) {
101257               this$1._minDe = de;
101258               this$1._minIndex = i;
101259               this$1._minCoord = coord[i];
101260             }
101261           }
101262         };
101263         RightmostEdgeFinder.prototype.findRightmostEdgeAtNode = function findRightmostEdgeAtNode () {
101264           var node = this._minDe.getNode();
101265           var star = node.getEdges();
101266           this._minDe = star.getRightmostEdge();
101267           if (!this._minDe.isForward()) {
101268             this._minDe = this._minDe.getSym();
101269             this._minIndex = this._minDe.getEdge().getCoordinates().length - 1;
101270           }
101271         };
101272         RightmostEdgeFinder.prototype.findEdge = function findEdge (dirEdgeList) {
101273             var this$1 = this;
101274
101275           for (var i = dirEdgeList.iterator(); i.hasNext();) {
101276             var de = i.next();
101277             if (!de.isForward()) { continue }
101278             this$1.checkForRightmostCoordinate(de);
101279           }
101280           Assert.isTrue(this._minIndex !== 0 || this._minCoord.equals(this._minDe.getCoordinate()), 'inconsistency in rightmost processing');
101281           if (this._minIndex === 0) {
101282             this.findRightmostEdgeAtNode();
101283           } else {
101284             this.findRightmostEdgeAtVertex();
101285           }
101286           this._orientedDe = this._minDe;
101287           var rightmostSide = this.getRightmostSide(this._minDe, this._minIndex);
101288           if (rightmostSide === Position.LEFT) {
101289             this._orientedDe = this._minDe.getSym();
101290           }
101291         };
101292         RightmostEdgeFinder.prototype.interfaces_ = function interfaces_ () {
101293           return []
101294         };
101295         RightmostEdgeFinder.prototype.getClass = function getClass () {
101296           return RightmostEdgeFinder
101297         };
101298
101299         var TopologyException = (function (RuntimeException$$1) {
101300           function TopologyException (msg, pt) {
101301             RuntimeException$$1.call(this, TopologyException.msgWithCoord(msg, pt));
101302             this.pt = pt ? new Coordinate(pt) : null;
101303             this.name = 'TopologyException';
101304           }
101305
101306           if ( RuntimeException$$1 ) { TopologyException.__proto__ = RuntimeException$$1; }
101307           TopologyException.prototype = Object.create( RuntimeException$$1 && RuntimeException$$1.prototype );
101308           TopologyException.prototype.constructor = TopologyException;
101309           TopologyException.prototype.getCoordinate = function getCoordinate () {
101310             return this.pt
101311           };
101312           TopologyException.prototype.interfaces_ = function interfaces_ () {
101313             return []
101314           };
101315           TopologyException.prototype.getClass = function getClass () {
101316             return TopologyException
101317           };
101318           TopologyException.msgWithCoord = function msgWithCoord (msg, pt) {
101319             if (!pt) { return msg + ' [ ' + pt + ' ]' }
101320             return msg
101321           };
101322
101323           return TopologyException;
101324         }(RuntimeException));
101325
101326         var LinkedList = function LinkedList () {
101327           this.array_ = [];
101328         };
101329         LinkedList.prototype.addLast = function addLast (e) {
101330           this.array_.push(e);
101331         };
101332         LinkedList.prototype.removeFirst = function removeFirst () {
101333           return this.array_.shift()
101334         };
101335         LinkedList.prototype.isEmpty = function isEmpty () {
101336           return this.array_.length === 0
101337         };
101338
101339         var BufferSubgraph = function BufferSubgraph () {
101340           this._finder = null;
101341           this._dirEdgeList = new ArrayList();
101342           this._nodes = new ArrayList();
101343           this._rightMostCoord = null;
101344           this._env = null;
101345           this._finder = new RightmostEdgeFinder();
101346         };
101347         BufferSubgraph.prototype.clearVisitedEdges = function clearVisitedEdges () {
101348           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101349             var de = it.next();
101350             de.setVisited(false);
101351           }
101352         };
101353         BufferSubgraph.prototype.getRightmostCoordinate = function getRightmostCoordinate () {
101354           return this._rightMostCoord
101355         };
101356         BufferSubgraph.prototype.computeNodeDepth = function computeNodeDepth (n) {
101357             var this$1 = this;
101358
101359           var startEdge = null;
101360           for (var i = n.getEdges().iterator(); i.hasNext();) {
101361             var de = i.next();
101362             if (de.isVisited() || de.getSym().isVisited()) {
101363               startEdge = de;
101364               break
101365             }
101366           }
101367           if (startEdge === null) { throw new TopologyException('unable to find edge to compute depths at ' + n.getCoordinate()) }
101368           n.getEdges().computeDepths(startEdge);
101369           for (var i$1 = n.getEdges().iterator(); i$1.hasNext();) {
101370             var de$1 = i$1.next();
101371             de$1.setVisited(true);
101372             this$1.copySymDepths(de$1);
101373           }
101374         };
101375         BufferSubgraph.prototype.computeDepth = function computeDepth (outsideDepth) {
101376           this.clearVisitedEdges();
101377           var de = this._finder.getEdge();
101378           // const n = de.getNode()
101379           // const label = de.getLabel()
101380           de.setEdgeDepths(Position.RIGHT, outsideDepth);
101381           this.copySymDepths(de);
101382           this.computeDepths(de);
101383         };
101384         BufferSubgraph.prototype.create = function create (node) {
101385           this.addReachable(node);
101386           this._finder.findEdge(this._dirEdgeList);
101387           this._rightMostCoord = this._finder.getCoordinate();
101388         };
101389         BufferSubgraph.prototype.findResultEdges = function findResultEdges () {
101390           for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101391             var de = it.next();
101392             if (de.getDepth(Position.RIGHT) >= 1 && de.getDepth(Position.LEFT) <= 0 && !de.isInteriorAreaEdge()) {
101393               de.setInResult(true);
101394             }
101395           }
101396         };
101397         BufferSubgraph.prototype.computeDepths = function computeDepths (startEdge) {
101398             var this$1 = this;
101399
101400           var nodesVisited = new HashSet();
101401           var nodeQueue = new LinkedList();
101402           var startNode = startEdge.getNode();
101403           nodeQueue.addLast(startNode);
101404           nodesVisited.add(startNode);
101405           startEdge.setVisited(true);
101406           while (!nodeQueue.isEmpty()) {
101407             var n = nodeQueue.removeFirst();
101408             nodesVisited.add(n);
101409             this$1.computeNodeDepth(n);
101410             for (var i = n.getEdges().iterator(); i.hasNext();) {
101411               var de = i.next();
101412               var sym = de.getSym();
101413               if (sym.isVisited()) { continue }
101414               var adjNode = sym.getNode();
101415               if (!nodesVisited.contains(adjNode)) {
101416                 nodeQueue.addLast(adjNode);
101417                 nodesVisited.add(adjNode);
101418               }
101419             }
101420           }
101421         };
101422         BufferSubgraph.prototype.compareTo = function compareTo (o) {
101423           var graph = o;
101424           if (this._rightMostCoord.x < graph._rightMostCoord.x) {
101425             return -1
101426           }
101427           if (this._rightMostCoord.x > graph._rightMostCoord.x) {
101428             return 1
101429           }
101430           return 0
101431         };
101432         BufferSubgraph.prototype.getEnvelope = function getEnvelope () {
101433           if (this._env === null) {
101434             var edgeEnv = new Envelope();
101435             for (var it = this._dirEdgeList.iterator(); it.hasNext();) {
101436               var dirEdge = it.next();
101437               var pts = dirEdge.getEdge().getCoordinates();
101438               for (var i = 0; i < pts.length - 1; i++) {
101439                 edgeEnv.expandToInclude(pts[i]);
101440               }
101441             }
101442             this._env = edgeEnv;
101443           }
101444           return this._env
101445         };
101446         BufferSubgraph.prototype.addReachable = function addReachable (startNode) {
101447             var this$1 = this;
101448
101449           var nodeStack = new Stack();
101450           nodeStack.add(startNode);
101451           while (!nodeStack.empty()) {
101452             var node = nodeStack.pop();
101453             this$1.add(node, nodeStack);
101454           }
101455         };
101456         BufferSubgraph.prototype.copySymDepths = function copySymDepths (de) {
101457           var sym = de.getSym();
101458           sym.setDepth(Position.LEFT, de.getDepth(Position.RIGHT));
101459           sym.setDepth(Position.RIGHT, de.getDepth(Position.LEFT));
101460         };
101461         BufferSubgraph.prototype.add = function add (node, nodeStack) {
101462             var this$1 = this;
101463
101464           node.setVisited(true);
101465           this._nodes.add(node);
101466           for (var i = node.getEdges().iterator(); i.hasNext();) {
101467             var de = i.next();
101468             this$1._dirEdgeList.add(de);
101469             var sym = de.getSym();
101470             var symNode = sym.getNode();
101471             if (!symNode.isVisited()) { nodeStack.push(symNode); }
101472           }
101473         };
101474         BufferSubgraph.prototype.getNodes = function getNodes () {
101475           return this._nodes
101476         };
101477         BufferSubgraph.prototype.getDirectedEdges = function getDirectedEdges () {
101478           return this._dirEdgeList
101479         };
101480         BufferSubgraph.prototype.interfaces_ = function interfaces_ () {
101481           return [Comparable]
101482         };
101483         BufferSubgraph.prototype.getClass = function getClass () {
101484           return BufferSubgraph
101485         };
101486
101487         var TopologyLocation = function TopologyLocation () {
101488           var this$1 = this;
101489
101490           this.location = null;
101491           if (arguments.length === 1) {
101492             if (arguments[0] instanceof Array) {
101493               var location = arguments[0];
101494               this.init(location.length);
101495             } else if (Number.isInteger(arguments[0])) {
101496               var on = arguments[0];
101497               this.init(1);
101498               this.location[Position.ON] = on;
101499             } else if (arguments[0] instanceof TopologyLocation) {
101500               var gl = arguments[0];
101501               this.init(gl.location.length);
101502               if (gl !== null) {
101503                 for (var i = 0; i < this.location.length; i++) {
101504                   this$1.location[i] = gl.location[i];
101505                 }
101506               }
101507             }
101508           } else if (arguments.length === 3) {
101509             var on$1 = arguments[0];
101510             var left = arguments[1];
101511             var right = arguments[2];
101512             this.init(3);
101513             this.location[Position.ON] = on$1;
101514             this.location[Position.LEFT] = left;
101515             this.location[Position.RIGHT] = right;
101516           }
101517         };
101518         TopologyLocation.prototype.setAllLocations = function setAllLocations (locValue) {
101519             var this$1 = this;
101520
101521           for (var i = 0; i < this.location.length; i++) {
101522             this$1.location[i] = locValue;
101523           }
101524         };
101525         TopologyLocation.prototype.isNull = function isNull () {
101526             var this$1 = this;
101527
101528           for (var i = 0; i < this.location.length; i++) {
101529             if (this$1.location[i] !== Location.NONE) { return false }
101530           }
101531           return true
101532         };
101533         TopologyLocation.prototype.setAllLocationsIfNull = function setAllLocationsIfNull (locValue) {
101534             var this$1 = this;
101535
101536           for (var i = 0; i < this.location.length; i++) {
101537             if (this$1.location[i] === Location.NONE) { this$1.location[i] = locValue; }
101538           }
101539         };
101540         TopologyLocation.prototype.isLine = function isLine () {
101541           return this.location.length === 1
101542         };
101543         TopologyLocation.prototype.merge = function merge (gl) {
101544             var this$1 = this;
101545
101546           if (gl.location.length > this.location.length) {
101547             var newLoc = new Array(3).fill(null);
101548             newLoc[Position.ON] = this.location[Position.ON];
101549             newLoc[Position.LEFT] = Location.NONE;
101550             newLoc[Position.RIGHT] = Location.NONE;
101551             this.location = newLoc;
101552           }
101553           for (var i = 0; i < this.location.length; i++) {
101554             if (this$1.location[i] === Location.NONE && i < gl.location.length) { this$1.location[i] = gl.location[i]; }
101555           }
101556         };
101557         TopologyLocation.prototype.getLocations = function getLocations () {
101558           return this.location
101559         };
101560         TopologyLocation.prototype.flip = function flip () {
101561           if (this.location.length <= 1) { return null }
101562           var temp = this.location[Position.LEFT];
101563           this.location[Position.LEFT] = this.location[Position.RIGHT];
101564           this.location[Position.RIGHT] = temp;
101565         };
101566         TopologyLocation.prototype.toString = function toString () {
101567           var buf = new StringBuffer();
101568           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.LEFT])); }
101569           buf.append(Location.toLocationSymbol(this.location[Position.ON]));
101570           if (this.location.length > 1) { buf.append(Location.toLocationSymbol(this.location[Position.RIGHT])); }
101571           return buf.toString()
101572         };
101573         TopologyLocation.prototype.setLocations = function setLocations (on, left, right) {
101574           this.location[Position.ON] = on;
101575           this.location[Position.LEFT] = left;
101576           this.location[Position.RIGHT] = right;
101577         };
101578         TopologyLocation.prototype.get = function get (posIndex) {
101579           if (posIndex < this.location.length) { return this.location[posIndex] }
101580           return Location.NONE
101581         };
101582         TopologyLocation.prototype.isArea = function isArea () {
101583           return this.location.length > 1
101584         };
101585         TopologyLocation.prototype.isAnyNull = function isAnyNull () {
101586             var this$1 = this;
101587
101588           for (var i = 0; i < this.location.length; i++) {
101589             if (this$1.location[i] === Location.NONE) { return true }
101590           }
101591           return false
101592         };
101593         TopologyLocation.prototype.setLocation = function setLocation () {
101594           if (arguments.length === 1) {
101595             var locValue = arguments[0];
101596             this.setLocation(Position.ON, locValue);
101597           } else if (arguments.length === 2) {
101598             var locIndex = arguments[0];
101599             var locValue$1 = arguments[1];
101600             this.location[locIndex] = locValue$1;
101601           }
101602         };
101603         TopologyLocation.prototype.init = function init (size) {
101604           this.location = new Array(size).fill(null);
101605           this.setAllLocations(Location.NONE);
101606         };
101607         TopologyLocation.prototype.isEqualOnSide = function isEqualOnSide (le, locIndex) {
101608           return this.location[locIndex] === le.location[locIndex]
101609         };
101610         TopologyLocation.prototype.allPositionsEqual = function allPositionsEqual (loc) {
101611             var this$1 = this;
101612
101613           for (var i = 0; i < this.location.length; i++) {
101614             if (this$1.location[i] !== loc) { return false }
101615           }
101616           return true
101617         };
101618         TopologyLocation.prototype.interfaces_ = function interfaces_ () {
101619           return []
101620         };
101621         TopologyLocation.prototype.getClass = function getClass () {
101622           return TopologyLocation
101623         };
101624
101625         var Label = function Label () {
101626           this.elt = new Array(2).fill(null);
101627           if (arguments.length === 1) {
101628             if (Number.isInteger(arguments[0])) {
101629               var onLoc = arguments[0];
101630               this.elt[0] = new TopologyLocation(onLoc);
101631               this.elt[1] = new TopologyLocation(onLoc);
101632             } else if (arguments[0] instanceof Label) {
101633               var lbl = arguments[0];
101634               this.elt[0] = new TopologyLocation(lbl.elt[0]);
101635               this.elt[1] = new TopologyLocation(lbl.elt[1]);
101636             }
101637           } else if (arguments.length === 2) {
101638             var geomIndex = arguments[0];
101639             var onLoc$1 = arguments[1];
101640             this.elt[0] = new TopologyLocation(Location.NONE);
101641             this.elt[1] = new TopologyLocation(Location.NONE);
101642             this.elt[geomIndex].setLocation(onLoc$1);
101643           } else if (arguments.length === 3) {
101644             var onLoc$2 = arguments[0];
101645             var leftLoc = arguments[1];
101646             var rightLoc = arguments[2];
101647             this.elt[0] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101648             this.elt[1] = new TopologyLocation(onLoc$2, leftLoc, rightLoc);
101649           } else if (arguments.length === 4) {
101650             var geomIndex$1 = arguments[0];
101651             var onLoc$3 = arguments[1];
101652             var leftLoc$1 = arguments[2];
101653             var rightLoc$1 = arguments[3];
101654             this.elt[0] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101655             this.elt[1] = new TopologyLocation(Location.NONE, Location.NONE, Location.NONE);
101656             this.elt[geomIndex$1].setLocations(onLoc$3, leftLoc$1, rightLoc$1);
101657           }
101658         };
101659         Label.prototype.getGeometryCount = function getGeometryCount () {
101660           var count = 0;
101661           if (!this.elt[0].isNull()) { count++; }
101662           if (!this.elt[1].isNull()) { count++; }
101663           return count
101664         };
101665         Label.prototype.setAllLocations = function setAllLocations (geomIndex, location) {
101666           this.elt[geomIndex].setAllLocations(location);
101667         };
101668         Label.prototype.isNull = function isNull (geomIndex) {
101669           return this.elt[geomIndex].isNull()
101670         };
101671         Label.prototype.setAllLocationsIfNull = function setAllLocationsIfNull () {
101672           if (arguments.length === 1) {
101673             var location = arguments[0];
101674             this.setAllLocationsIfNull(0, location);
101675             this.setAllLocationsIfNull(1, location);
101676           } else if (arguments.length === 2) {
101677             var geomIndex = arguments[0];
101678             var location$1 = arguments[1];
101679             this.elt[geomIndex].setAllLocationsIfNull(location$1);
101680           }
101681         };
101682         Label.prototype.isLine = function isLine (geomIndex) {
101683           return this.elt[geomIndex].isLine()
101684         };
101685         Label.prototype.merge = function merge (lbl) {
101686             var this$1 = this;
101687
101688           for (var i = 0; i < 2; i++) {
101689             if (this$1.elt[i] === null && lbl.elt[i] !== null) {
101690               this$1.elt[i] = new TopologyLocation(lbl.elt[i]);
101691             } else {
101692               this$1.elt[i].merge(lbl.elt[i]);
101693             }
101694           }
101695         };
101696         Label.prototype.flip = function flip () {
101697           this.elt[0].flip();
101698           this.elt[1].flip();
101699         };
101700         Label.prototype.getLocation = function getLocation () {
101701           if (arguments.length === 1) {
101702             var geomIndex = arguments[0];
101703             return this.elt[geomIndex].get(Position.ON)
101704           } else if (arguments.length === 2) {
101705             var geomIndex$1 = arguments[0];
101706             var posIndex = arguments[1];
101707             return this.elt[geomIndex$1].get(posIndex)
101708           }
101709         };
101710         Label.prototype.toString = function toString () {
101711           var buf = new StringBuffer();
101712           if (this.elt[0] !== null) {
101713             buf.append('A:');
101714             buf.append(this.elt[0].toString());
101715           }
101716           if (this.elt[1] !== null) {
101717             buf.append(' B:');
101718             buf.append(this.elt[1].toString());
101719           }
101720           return buf.toString()
101721         };
101722         Label.prototype.isArea = function isArea () {
101723           if (arguments.length === 0) {
101724             return this.elt[0].isArea() || this.elt[1].isArea()
101725           } else if (arguments.length === 1) {
101726             var geomIndex = arguments[0];
101727             return this.elt[geomIndex].isArea()
101728           }
101729         };
101730         Label.prototype.isAnyNull = function isAnyNull (geomIndex) {
101731           return this.elt[geomIndex].isAnyNull()
101732         };
101733         Label.prototype.setLocation = function setLocation () {
101734           if (arguments.length === 2) {
101735             var geomIndex = arguments[0];
101736             var location = arguments[1];
101737             this.elt[geomIndex].setLocation(Position.ON, location);
101738           } else if (arguments.length === 3) {
101739             var geomIndex$1 = arguments[0];
101740             var posIndex = arguments[1];
101741             var location$1 = arguments[2];
101742             this.elt[geomIndex$1].setLocation(posIndex, location$1);
101743           }
101744         };
101745         Label.prototype.isEqualOnSide = function isEqualOnSide (lbl, side) {
101746           return this.elt[0].isEqualOnSide(lbl.elt[0], side) && this.elt[1].isEqualOnSide(lbl.elt[1], side)
101747         };
101748         Label.prototype.allPositionsEqual = function allPositionsEqual (geomIndex, loc) {
101749           return this.elt[geomIndex].allPositionsEqual(loc)
101750         };
101751         Label.prototype.toLine = function toLine (geomIndex) {
101752           if (this.elt[geomIndex].isArea()) { this.elt[geomIndex] = new TopologyLocation(this.elt[geomIndex].location[0]); }
101753         };
101754         Label.prototype.interfaces_ = function interfaces_ () {
101755           return []
101756         };
101757         Label.prototype.getClass = function getClass () {
101758           return Label
101759         };
101760         Label.toLineLabel = function toLineLabel (label) {
101761           var lineLabel = new Label(Location.NONE);
101762           for (var i = 0; i < 2; i++) {
101763             lineLabel.setLocation(i, label.getLocation(i));
101764           }
101765           return lineLabel
101766         };
101767
101768         var EdgeRing = function EdgeRing () {
101769           this._startDe = null;
101770           this._maxNodeDegree = -1;
101771           this._edges = new ArrayList();
101772           this._pts = new ArrayList();
101773           this._label = new Label(Location.NONE);
101774           this._ring = null;
101775           this._isHole = null;
101776           this._shell = null;
101777           this._holes = new ArrayList();
101778           this._geometryFactory = null;
101779           var start = arguments[0];
101780           var geometryFactory = arguments[1];
101781           this._geometryFactory = geometryFactory;
101782           this.computePoints(start);
101783           this.computeRing();
101784         };
101785         EdgeRing.prototype.computeRing = function computeRing () {
101786             var this$1 = this;
101787
101788           if (this._ring !== null) { return null }
101789           var coord = new Array(this._pts.size()).fill(null);
101790           for (var i = 0; i < this._pts.size(); i++) {
101791             coord[i] = this$1._pts.get(i);
101792           }
101793           this._ring = this._geometryFactory.createLinearRing(coord);
101794           this._isHole = CGAlgorithms.isCCW(this._ring.getCoordinates());
101795         };
101796         EdgeRing.prototype.isIsolated = function isIsolated () {
101797           return this._label.getGeometryCount() === 1
101798         };
101799         EdgeRing.prototype.computePoints = function computePoints (start) {
101800             var this$1 = this;
101801
101802           this._startDe = start;
101803           var de = start;
101804           var isFirstEdge = true;
101805           do {
101806             if (de === null) { throw new TopologyException('Found null DirectedEdge') }
101807             if (de.getEdgeRing() === this$1) { throw new TopologyException('Directed Edge visited twice during ring-building at ' + de.getCoordinate()) }
101808             this$1._edges.add(de);
101809             var label = de.getLabel();
101810             Assert.isTrue(label.isArea());
101811             this$1.mergeLabel(label);
101812             this$1.addPoints(de.getEdge(), de.isForward(), isFirstEdge);
101813             isFirstEdge = false;
101814             this$1.setEdgeRing(de, this$1);
101815             de = this$1.getNext(de);
101816           } while (de !== this._startDe)
101817         };
101818         EdgeRing.prototype.getLinearRing = function getLinearRing () {
101819           return this._ring
101820         };
101821         EdgeRing.prototype.getCoordinate = function getCoordinate (i) {
101822           return this._pts.get(i)
101823         };
101824         EdgeRing.prototype.computeMaxNodeDegree = function computeMaxNodeDegree () {
101825             var this$1 = this;
101826
101827           this._maxNodeDegree = 0;
101828           var de = this._startDe;
101829           do {
101830             var node = de.getNode();
101831             var degree = node.getEdges().getOutgoingDegree(this$1);
101832             if (degree > this$1._maxNodeDegree) { this$1._maxNodeDegree = degree; }
101833             de = this$1.getNext(de);
101834           } while (de !== this._startDe)
101835           this._maxNodeDegree *= 2;
101836         };
101837         EdgeRing.prototype.addPoints = function addPoints (edge, isForward, isFirstEdge) {
101838             var this$1 = this;
101839
101840           var edgePts = edge.getCoordinates();
101841           if (isForward) {
101842             var startIndex = 1;
101843             if (isFirstEdge) { startIndex = 0; }
101844             for (var i = startIndex; i < edgePts.length; i++) {
101845               this$1._pts.add(edgePts[i]);
101846             }
101847           } else {
101848             var startIndex$1 = edgePts.length - 2;
101849             if (isFirstEdge) { startIndex$1 = edgePts.length - 1; }
101850             for (var i$1 = startIndex$1; i$1 >= 0; i$1--) {
101851               this$1._pts.add(edgePts[i$1]);
101852             }
101853           }
101854         };
101855         EdgeRing.prototype.isHole = function isHole () {
101856           return this._isHole
101857         };
101858         EdgeRing.prototype.setInResult = function setInResult () {
101859           var de = this._startDe;
101860           do {
101861             de.getEdge().setInResult(true);
101862             de = de.getNext();
101863           } while (de !== this._startDe)
101864         };
101865         EdgeRing.prototype.containsPoint = function containsPoint (p) {
101866           var shell = this.getLinearRing();
101867           var env = shell.getEnvelopeInternal();
101868           if (!env.contains(p)) { return false }
101869           if (!CGAlgorithms.isPointInRing(p, shell.getCoordinates())) { return false }
101870           for (var i = this._holes.iterator(); i.hasNext();) {
101871             var hole = i.next();
101872             if (hole.containsPoint(p)) { return false }
101873           }
101874           return true
101875         };
101876         EdgeRing.prototype.addHole = function addHole (ring) {
101877           this._holes.add(ring);
101878         };
101879         EdgeRing.prototype.isShell = function isShell () {
101880           return this._shell === null
101881         };
101882         EdgeRing.prototype.getLabel = function getLabel () {
101883           return this._label
101884         };
101885         EdgeRing.prototype.getEdges = function getEdges () {
101886           return this._edges
101887         };
101888         EdgeRing.prototype.getMaxNodeDegree = function getMaxNodeDegree () {
101889           if (this._maxNodeDegree < 0) { this.computeMaxNodeDegree(); }
101890           return this._maxNodeDegree
101891         };
101892         EdgeRing.prototype.getShell = function getShell () {
101893           return this._shell
101894         };
101895         EdgeRing.prototype.mergeLabel = function mergeLabel () {
101896           if (arguments.length === 1) {
101897             var deLabel = arguments[0];
101898             this.mergeLabel(deLabel, 0);
101899             this.mergeLabel(deLabel, 1);
101900           } else if (arguments.length === 2) {
101901             var deLabel$1 = arguments[0];
101902             var geomIndex = arguments[1];
101903             var loc = deLabel$1.getLocation(geomIndex, Position.RIGHT);
101904             if (loc === Location.NONE) { return null }
101905             if (this._label.getLocation(geomIndex) === Location.NONE) {
101906               this._label.setLocation(geomIndex, loc);
101907               return null
101908             }
101909           }
101910         };
101911         EdgeRing.prototype.setShell = function setShell (shell) {
101912           this._shell = shell;
101913           if (shell !== null) { shell.addHole(this); }
101914         };
101915         EdgeRing.prototype.toPolygon = function toPolygon (geometryFactory) {
101916             var this$1 = this;
101917
101918           var holeLR = new Array(this._holes.size()).fill(null);
101919           for (var i = 0; i < this._holes.size(); i++) {
101920             holeLR[i] = this$1._holes.get(i).getLinearRing();
101921           }
101922           var poly = geometryFactory.createPolygon(this.getLinearRing(), holeLR);
101923           return poly
101924         };
101925         EdgeRing.prototype.interfaces_ = function interfaces_ () {
101926           return []
101927         };
101928         EdgeRing.prototype.getClass = function getClass () {
101929           return EdgeRing
101930         };
101931
101932         var MinimalEdgeRing = (function (EdgeRing$$1) {
101933           function MinimalEdgeRing () {
101934             var start = arguments[0];
101935             var geometryFactory = arguments[1];
101936             EdgeRing$$1.call(this, start, geometryFactory);
101937           }
101938
101939           if ( EdgeRing$$1 ) { MinimalEdgeRing.__proto__ = EdgeRing$$1; }
101940           MinimalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101941           MinimalEdgeRing.prototype.constructor = MinimalEdgeRing;
101942           MinimalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101943             de.setMinEdgeRing(er);
101944           };
101945           MinimalEdgeRing.prototype.getNext = function getNext (de) {
101946             return de.getNextMin()
101947           };
101948           MinimalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101949             return []
101950           };
101951           MinimalEdgeRing.prototype.getClass = function getClass () {
101952             return MinimalEdgeRing
101953           };
101954
101955           return MinimalEdgeRing;
101956         }(EdgeRing));
101957
101958         var MaximalEdgeRing = (function (EdgeRing$$1) {
101959           function MaximalEdgeRing () {
101960             var start = arguments[0];
101961             var geometryFactory = arguments[1];
101962             EdgeRing$$1.call(this, start, geometryFactory);
101963           }
101964
101965           if ( EdgeRing$$1 ) { MaximalEdgeRing.__proto__ = EdgeRing$$1; }
101966           MaximalEdgeRing.prototype = Object.create( EdgeRing$$1 && EdgeRing$$1.prototype );
101967           MaximalEdgeRing.prototype.constructor = MaximalEdgeRing;
101968           MaximalEdgeRing.prototype.buildMinimalRings = function buildMinimalRings () {
101969             var this$1 = this;
101970
101971             var minEdgeRings = new ArrayList();
101972             var de = this._startDe;
101973             do {
101974               if (de.getMinEdgeRing() === null) {
101975                 var minEr = new MinimalEdgeRing(de, this$1._geometryFactory);
101976                 minEdgeRings.add(minEr);
101977               }
101978               de = de.getNext();
101979             } while (de !== this._startDe)
101980             return minEdgeRings
101981           };
101982           MaximalEdgeRing.prototype.setEdgeRing = function setEdgeRing (de, er) {
101983             de.setEdgeRing(er);
101984           };
101985           MaximalEdgeRing.prototype.linkDirectedEdgesForMinimalEdgeRings = function linkDirectedEdgesForMinimalEdgeRings () {
101986             var this$1 = this;
101987
101988             var de = this._startDe;
101989             do {
101990               var node = de.getNode();
101991               node.getEdges().linkMinimalDirectedEdges(this$1);
101992               de = de.getNext();
101993             } while (de !== this._startDe)
101994           };
101995           MaximalEdgeRing.prototype.getNext = function getNext (de) {
101996             return de.getNext()
101997           };
101998           MaximalEdgeRing.prototype.interfaces_ = function interfaces_ () {
101999             return []
102000           };
102001           MaximalEdgeRing.prototype.getClass = function getClass () {
102002             return MaximalEdgeRing
102003           };
102004
102005           return MaximalEdgeRing;
102006         }(EdgeRing));
102007
102008         var GraphComponent = function GraphComponent () {
102009           this._label = null;
102010           this._isInResult = false;
102011           this._isCovered = false;
102012           this._isCoveredSet = false;
102013           this._isVisited = false;
102014           if (arguments.length === 0) ; else if (arguments.length === 1) {
102015             var label = arguments[0];
102016             this._label = label;
102017           }
102018         };
102019         GraphComponent.prototype.setVisited = function setVisited (isVisited) {
102020           this._isVisited = isVisited;
102021         };
102022         GraphComponent.prototype.setInResult = function setInResult (isInResult) {
102023           this._isInResult = isInResult;
102024         };
102025         GraphComponent.prototype.isCovered = function isCovered () {
102026           return this._isCovered
102027         };
102028         GraphComponent.prototype.isCoveredSet = function isCoveredSet () {
102029           return this._isCoveredSet
102030         };
102031         GraphComponent.prototype.setLabel = function setLabel (label) {
102032           this._label = label;
102033         };
102034         GraphComponent.prototype.getLabel = function getLabel () {
102035           return this._label
102036         };
102037         GraphComponent.prototype.setCovered = function setCovered (isCovered) {
102038           this._isCovered = isCovered;
102039           this._isCoveredSet = true;
102040         };
102041         GraphComponent.prototype.updateIM = function updateIM (im) {
102042           Assert.isTrue(this._label.getGeometryCount() >= 2, 'found partial label');
102043           this.computeIM(im);
102044         };
102045         GraphComponent.prototype.isInResult = function isInResult () {
102046           return this._isInResult
102047         };
102048         GraphComponent.prototype.isVisited = function isVisited () {
102049           return this._isVisited
102050         };
102051         GraphComponent.prototype.interfaces_ = function interfaces_ () {
102052           return []
102053         };
102054         GraphComponent.prototype.getClass = function getClass () {
102055           return GraphComponent
102056         };
102057
102058         var Node$1 = (function (GraphComponent$$1) {
102059           function Node () {
102060             GraphComponent$$1.call(this);
102061             this._coord = null;
102062             this._edges = null;
102063             var coord = arguments[0];
102064             var edges = arguments[1];
102065             this._coord = coord;
102066             this._edges = edges;
102067             this._label = new Label(0, Location.NONE);
102068           }
102069
102070           if ( GraphComponent$$1 ) { Node.__proto__ = GraphComponent$$1; }
102071           Node.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
102072           Node.prototype.constructor = Node;
102073           Node.prototype.isIncidentEdgeInResult = function isIncidentEdgeInResult () {
102074             for (var it = this.getEdges().getEdges().iterator(); it.hasNext();) {
102075               var de = it.next();
102076               if (de.getEdge().isInResult()) { return true }
102077             }
102078             return false
102079           };
102080           Node.prototype.isIsolated = function isIsolated () {
102081             return this._label.getGeometryCount() === 1
102082           };
102083           Node.prototype.getCoordinate = function getCoordinate () {
102084             return this._coord
102085           };
102086           Node.prototype.print = function print (out) {
102087             out.println('node ' + this._coord + ' lbl: ' + this._label);
102088           };
102089           Node.prototype.computeIM = function computeIM (im) {};
102090           Node.prototype.computeMergedLocation = function computeMergedLocation (label2, eltIndex) {
102091             var loc = Location.NONE;
102092             loc = this._label.getLocation(eltIndex);
102093             if (!label2.isNull(eltIndex)) {
102094               var nLoc = label2.getLocation(eltIndex);
102095               if (loc !== Location.BOUNDARY) { loc = nLoc; }
102096             }
102097             return loc
102098           };
102099           Node.prototype.setLabel = function setLabel () {
102100             if (arguments.length === 2) {
102101               var argIndex = arguments[0];
102102               var onLocation = arguments[1];
102103               if (this._label === null) {
102104                 this._label = new Label(argIndex, onLocation);
102105               } else { this._label.setLocation(argIndex, onLocation); }
102106             } else { return GraphComponent$$1.prototype.setLabel.apply(this, arguments) }
102107           };
102108           Node.prototype.getEdges = function getEdges () {
102109             return this._edges
102110           };
102111           Node.prototype.mergeLabel = function mergeLabel () {
102112             var this$1 = this;
102113
102114             if (arguments[0] instanceof Node) {
102115               var n = arguments[0];
102116               this.mergeLabel(n._label);
102117             } else if (arguments[0] instanceof Label) {
102118               var label2 = arguments[0];
102119               for (var i = 0; i < 2; i++) {
102120                 var loc = this$1.computeMergedLocation(label2, i);
102121                 var thisLoc = this$1._label.getLocation(i);
102122                 if (thisLoc === Location.NONE) { this$1._label.setLocation(i, loc); }
102123               }
102124             }
102125           };
102126           Node.prototype.add = function add (e) {
102127             this._edges.insert(e);
102128             e.setNode(this);
102129           };
102130           Node.prototype.setLabelBoundary = function setLabelBoundary (argIndex) {
102131             if (this._label === null) { return null }
102132             var loc = Location.NONE;
102133             if (this._label !== null) { loc = this._label.getLocation(argIndex); }
102134             var newLoc = null;
102135             switch (loc) {
102136               case Location.BOUNDARY:
102137                 newLoc = Location.INTERIOR;
102138                 break
102139               case Location.INTERIOR:
102140                 newLoc = Location.BOUNDARY;
102141                 break
102142               default:
102143                 newLoc = Location.BOUNDARY;
102144                 break
102145             }
102146             this._label.setLocation(argIndex, newLoc);
102147           };
102148           Node.prototype.interfaces_ = function interfaces_ () {
102149             return []
102150           };
102151           Node.prototype.getClass = function getClass () {
102152             return Node
102153           };
102154
102155           return Node;
102156         }(GraphComponent));
102157
102158         var NodeMap = function NodeMap () {
102159           this.nodeMap = new TreeMap();
102160           this.nodeFact = null;
102161           var nodeFact = arguments[0];
102162           this.nodeFact = nodeFact;
102163         };
102164         NodeMap.prototype.find = function find (coord) {
102165           return this.nodeMap.get(coord)
102166         };
102167         NodeMap.prototype.addNode = function addNode () {
102168           if (arguments[0] instanceof Coordinate) {
102169             var coord = arguments[0];
102170             var node = this.nodeMap.get(coord);
102171             if (node === null) {
102172               node = this.nodeFact.createNode(coord);
102173               this.nodeMap.put(coord, node);
102174             }
102175             return node
102176           } else if (arguments[0] instanceof Node$1) {
102177             var n = arguments[0];
102178             var node$1 = this.nodeMap.get(n.getCoordinate());
102179             if (node$1 === null) {
102180               this.nodeMap.put(n.getCoordinate(), n);
102181               return n
102182             }
102183             node$1.mergeLabel(n);
102184             return node$1
102185           }
102186         };
102187         NodeMap.prototype.print = function print (out) {
102188           for (var it = this.iterator(); it.hasNext();) {
102189             var n = it.next();
102190             n.print(out);
102191           }
102192         };
102193         NodeMap.prototype.iterator = function iterator () {
102194           return this.nodeMap.values().iterator()
102195         };
102196         NodeMap.prototype.values = function values () {
102197           return this.nodeMap.values()
102198         };
102199         NodeMap.prototype.getBoundaryNodes = function getBoundaryNodes (geomIndex) {
102200           var bdyNodes = new ArrayList();
102201           for (var i = this.iterator(); i.hasNext();) {
102202             var node = i.next();
102203             if (node.getLabel().getLocation(geomIndex) === Location.BOUNDARY) { bdyNodes.add(node); }
102204           }
102205           return bdyNodes
102206         };
102207         NodeMap.prototype.add = function add (e) {
102208           var p = e.getCoordinate();
102209           var n = this.addNode(p);
102210           n.add(e);
102211         };
102212         NodeMap.prototype.interfaces_ = function interfaces_ () {
102213           return []
102214         };
102215         NodeMap.prototype.getClass = function getClass () {
102216           return NodeMap
102217         };
102218
102219         var Quadrant = function Quadrant () {};
102220
102221         var staticAccessors$21 = { NE: { configurable: true },NW: { configurable: true },SW: { configurable: true },SE: { configurable: true } };
102222
102223         Quadrant.prototype.interfaces_ = function interfaces_ () {
102224           return []
102225         };
102226         Quadrant.prototype.getClass = function getClass () {
102227           return Quadrant
102228         };
102229         Quadrant.isNorthern = function isNorthern (quad) {
102230           return quad === Quadrant.NE || quad === Quadrant.NW
102231         };
102232         Quadrant.isOpposite = function isOpposite (quad1, quad2) {
102233           if (quad1 === quad2) { return false }
102234           var diff = (quad1 - quad2 + 4) % 4;
102235           if (diff === 2) { return true }
102236           return false
102237         };
102238         Quadrant.commonHalfPlane = function commonHalfPlane (quad1, quad2) {
102239           if (quad1 === quad2) { return quad1 }
102240           var diff = (quad1 - quad2 + 4) % 4;
102241           if (diff === 2) { return -1 }
102242           var min = quad1 < quad2 ? quad1 : quad2;
102243           var max = quad1 > quad2 ? quad1 : quad2;
102244           if (min === 0 && max === 3) { return 3 }
102245           return min
102246         };
102247         Quadrant.isInHalfPlane = function isInHalfPlane (quad, halfPlane) {
102248           if (halfPlane === Quadrant.SE) {
102249             return quad === Quadrant.SE || quad === Quadrant.SW
102250           }
102251           return quad === halfPlane || quad === halfPlane + 1
102252         };
102253         Quadrant.quadrant = function quadrant () {
102254           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
102255             var dx = arguments[0];
102256             var dy = arguments[1];
102257             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the quadrant for point ( ' + dx + ', ' + dy + ' )') }
102258             if (dx >= 0.0) {
102259               if (dy >= 0.0) { return Quadrant.NE; } else { return Quadrant.SE }
102260             } else {
102261               if (dy >= 0.0) { return Quadrant.NW; } else { return Quadrant.SW }
102262             }
102263           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
102264             var p0 = arguments[0];
102265             var p1 = arguments[1];
102266             if (p1.x === p0.x && p1.y === p0.y) { throw new IllegalArgumentException('Cannot compute the quadrant for two identical points ' + p0) }
102267             if (p1.x >= p0.x) {
102268               if (p1.y >= p0.y) { return Quadrant.NE; } else { return Quadrant.SE }
102269             } else {
102270               if (p1.y >= p0.y) { return Quadrant.NW; } else { return Quadrant.SW }
102271             }
102272           }
102273         };
102274         staticAccessors$21.NE.get = function () { return 0 };
102275         staticAccessors$21.NW.get = function () { return 1 };
102276         staticAccessors$21.SW.get = function () { return 2 };
102277         staticAccessors$21.SE.get = function () { return 3 };
102278
102279         Object.defineProperties( Quadrant, staticAccessors$21 );
102280
102281         var EdgeEnd = function EdgeEnd () {
102282           this._edge = null;
102283           this._label = null;
102284           this._node = null;
102285           this._p0 = null;
102286           this._p1 = null;
102287           this._dx = null;
102288           this._dy = null;
102289           this._quadrant = null;
102290           if (arguments.length === 1) {
102291             var edge = arguments[0];
102292             this._edge = edge;
102293           } else if (arguments.length === 3) {
102294             var edge$1 = arguments[0];
102295             var p0 = arguments[1];
102296             var p1 = arguments[2];
102297             var label = null;
102298             this._edge = edge$1;
102299             this.init(p0, p1);
102300             this._label = label;
102301           } else if (arguments.length === 4) {
102302             var edge$2 = arguments[0];
102303             var p0$1 = arguments[1];
102304             var p1$1 = arguments[2];
102305             var label$1 = arguments[3];
102306             this._edge = edge$2;
102307             this.init(p0$1, p1$1);
102308             this._label = label$1;
102309           }
102310         };
102311         EdgeEnd.prototype.compareDirection = function compareDirection (e) {
102312           if (this._dx === e._dx && this._dy === e._dy) { return 0 }
102313           if (this._quadrant > e._quadrant) { return 1 }
102314           if (this._quadrant < e._quadrant) { return -1 }
102315           return CGAlgorithms.computeOrientation(e._p0, e._p1, this._p1)
102316         };
102317         EdgeEnd.prototype.getDy = function getDy () {
102318           return this._dy
102319         };
102320         EdgeEnd.prototype.getCoordinate = function getCoordinate () {
102321           return this._p0
102322         };
102323         EdgeEnd.prototype.setNode = function setNode (node) {
102324           this._node = node;
102325         };
102326         EdgeEnd.prototype.print = function print (out) {
102327           var angle = Math.atan2(this._dy, this._dx);
102328           var className = this.getClass().getName();
102329           var lastDotPos = className.lastIndexOf('.');
102330           var name = className.substring(lastDotPos + 1);
102331           out.print('  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label);
102332         };
102333         EdgeEnd.prototype.compareTo = function compareTo (obj) {
102334           var e = obj;
102335           return this.compareDirection(e)
102336         };
102337         EdgeEnd.prototype.getDirectedCoordinate = function getDirectedCoordinate () {
102338           return this._p1
102339         };
102340         EdgeEnd.prototype.getDx = function getDx () {
102341           return this._dx
102342         };
102343         EdgeEnd.prototype.getLabel = function getLabel () {
102344           return this._label
102345         };
102346         EdgeEnd.prototype.getEdge = function getEdge () {
102347           return this._edge
102348         };
102349         EdgeEnd.prototype.getQuadrant = function getQuadrant () {
102350           return this._quadrant
102351         };
102352         EdgeEnd.prototype.getNode = function getNode () {
102353           return this._node
102354         };
102355         EdgeEnd.prototype.toString = function toString () {
102356           var angle = Math.atan2(this._dy, this._dx);
102357           var className = this.getClass().getName();
102358           var lastDotPos = className.lastIndexOf('.');
102359           var name = className.substring(lastDotPos + 1);
102360           return '  ' + name + ': ' + this._p0 + ' - ' + this._p1 + ' ' + this._quadrant + ':' + angle + '   ' + this._label
102361         };
102362         EdgeEnd.prototype.computeLabel = function computeLabel (boundaryNodeRule) {};
102363         EdgeEnd.prototype.init = function init (p0, p1) {
102364           this._p0 = p0;
102365           this._p1 = p1;
102366           this._dx = p1.x - p0.x;
102367           this._dy = p1.y - p0.y;
102368           this._quadrant = Quadrant.quadrant(this._dx, this._dy);
102369           Assert.isTrue(!(this._dx === 0 && this._dy === 0), 'EdgeEnd with identical endpoints found');
102370         };
102371         EdgeEnd.prototype.interfaces_ = function interfaces_ () {
102372           return [Comparable]
102373         };
102374         EdgeEnd.prototype.getClass = function getClass () {
102375           return EdgeEnd
102376         };
102377
102378         var DirectedEdge = (function (EdgeEnd$$1) {
102379           function DirectedEdge () {
102380             var edge = arguments[0];
102381             var isForward = arguments[1];
102382             EdgeEnd$$1.call(this, edge);
102383             this._isForward = null;
102384             this._isInResult = false;
102385             this._isVisited = false;
102386             this._sym = null;
102387             this._next = null;
102388             this._nextMin = null;
102389             this._edgeRing = null;
102390             this._minEdgeRing = null;
102391             this._depth = [0, -999, -999];
102392             this._isForward = isForward;
102393             if (isForward) {
102394               this.init(edge.getCoordinate(0), edge.getCoordinate(1));
102395             } else {
102396               var n = edge.getNumPoints() - 1;
102397               this.init(edge.getCoordinate(n), edge.getCoordinate(n - 1));
102398             }
102399             this.computeDirectedLabel();
102400           }
102401
102402           if ( EdgeEnd$$1 ) { DirectedEdge.__proto__ = EdgeEnd$$1; }
102403           DirectedEdge.prototype = Object.create( EdgeEnd$$1 && EdgeEnd$$1.prototype );
102404           DirectedEdge.prototype.constructor = DirectedEdge;
102405           DirectedEdge.prototype.getNextMin = function getNextMin () {
102406             return this._nextMin
102407           };
102408           DirectedEdge.prototype.getDepth = function getDepth (position) {
102409             return this._depth[position]
102410           };
102411           DirectedEdge.prototype.setVisited = function setVisited (isVisited) {
102412             this._isVisited = isVisited;
102413           };
102414           DirectedEdge.prototype.computeDirectedLabel = function computeDirectedLabel () {
102415             this._label = new Label(this._edge.getLabel());
102416             if (!this._isForward) { this._label.flip(); }
102417           };
102418           DirectedEdge.prototype.getNext = function getNext () {
102419             return this._next
102420           };
102421           DirectedEdge.prototype.setDepth = function setDepth (position, depthVal) {
102422             if (this._depth[position] !== -999) {
102423               if (this._depth[position] !== depthVal) { throw new TopologyException('assigned depths do not match', this.getCoordinate()) }
102424             }
102425             this._depth[position] = depthVal;
102426           };
102427           DirectedEdge.prototype.isInteriorAreaEdge = function isInteriorAreaEdge () {
102428             var this$1 = this;
102429
102430             var isInteriorAreaEdge = true;
102431             for (var i = 0; i < 2; i++) {
102432               if (!(this$1._label.isArea(i) && this$1._label.getLocation(i, Position.LEFT) === Location.INTERIOR && this$1._label.getLocation(i, Position.RIGHT) === Location.INTERIOR)) {
102433                 isInteriorAreaEdge = false;
102434               }
102435             }
102436             return isInteriorAreaEdge
102437           };
102438           DirectedEdge.prototype.setNextMin = function setNextMin (nextMin) {
102439             this._nextMin = nextMin;
102440           };
102441           DirectedEdge.prototype.print = function print (out) {
102442             EdgeEnd$$1.prototype.print.call(this, out);
102443             out.print(' ' + this._depth[Position.LEFT] + '/' + this._depth[Position.RIGHT]);
102444             out.print(' (' + this.getDepthDelta() + ')');
102445             if (this._isInResult) { out.print(' inResult'); }
102446           };
102447           DirectedEdge.prototype.setMinEdgeRing = function setMinEdgeRing (minEdgeRing) {
102448             this._minEdgeRing = minEdgeRing;
102449           };
102450           DirectedEdge.prototype.isLineEdge = function isLineEdge () {
102451             var isLine = this._label.isLine(0) || this._label.isLine(1);
102452             var isExteriorIfArea0 = !this._label.isArea(0) || this._label.allPositionsEqual(0, Location.EXTERIOR);
102453             var isExteriorIfArea1 = !this._label.isArea(1) || this._label.allPositionsEqual(1, Location.EXTERIOR);
102454             return isLine && isExteriorIfArea0 && isExteriorIfArea1
102455           };
102456           DirectedEdge.prototype.setEdgeRing = function setEdgeRing (edgeRing) {
102457             this._edgeRing = edgeRing;
102458           };
102459           DirectedEdge.prototype.getMinEdgeRing = function getMinEdgeRing () {
102460             return this._minEdgeRing
102461           };
102462           DirectedEdge.prototype.getDepthDelta = function getDepthDelta () {
102463             var depthDelta = this._edge.getDepthDelta();
102464             if (!this._isForward) { depthDelta = -depthDelta; }
102465             return depthDelta
102466           };
102467           DirectedEdge.prototype.setInResult = function setInResult (isInResult) {
102468             this._isInResult = isInResult;
102469           };
102470           DirectedEdge.prototype.getSym = function getSym () {
102471             return this._sym
102472           };
102473           DirectedEdge.prototype.isForward = function isForward () {
102474             return this._isForward
102475           };
102476           DirectedEdge.prototype.getEdge = function getEdge () {
102477             return this._edge
102478           };
102479           DirectedEdge.prototype.printEdge = function printEdge (out) {
102480             this.print(out);
102481             out.print(' ');
102482             if (this._isForward) { this._edge.print(out); } else { this._edge.printReverse(out); }
102483           };
102484           DirectedEdge.prototype.setSym = function setSym (de) {
102485             this._sym = de;
102486           };
102487           DirectedEdge.prototype.setVisitedEdge = function setVisitedEdge (isVisited) {
102488             this.setVisited(isVisited);
102489             this._sym.setVisited(isVisited);
102490           };
102491           DirectedEdge.prototype.setEdgeDepths = function setEdgeDepths (position, depth) {
102492             var depthDelta = this.getEdge().getDepthDelta();
102493             if (!this._isForward) { depthDelta = -depthDelta; }
102494             var directionFactor = 1;
102495             if (position === Position.LEFT) { directionFactor = -1; }
102496             var oppositePos = Position.opposite(position);
102497             var delta = depthDelta * directionFactor;
102498             var oppositeDepth = depth + delta;
102499             this.setDepth(position, depth);
102500             this.setDepth(oppositePos, oppositeDepth);
102501           };
102502           DirectedEdge.prototype.getEdgeRing = function getEdgeRing () {
102503             return this._edgeRing
102504           };
102505           DirectedEdge.prototype.isInResult = function isInResult () {
102506             return this._isInResult
102507           };
102508           DirectedEdge.prototype.setNext = function setNext (next) {
102509             this._next = next;
102510           };
102511           DirectedEdge.prototype.isVisited = function isVisited () {
102512             return this._isVisited
102513           };
102514           DirectedEdge.prototype.interfaces_ = function interfaces_ () {
102515             return []
102516           };
102517           DirectedEdge.prototype.getClass = function getClass () {
102518             return DirectedEdge
102519           };
102520           DirectedEdge.depthFactor = function depthFactor (currLocation, nextLocation) {
102521             if (currLocation === Location.EXTERIOR && nextLocation === Location.INTERIOR) { return 1; } else if (currLocation === Location.INTERIOR && nextLocation === Location.EXTERIOR) { return -1 }
102522             return 0
102523           };
102524
102525           return DirectedEdge;
102526         }(EdgeEnd));
102527
102528         var NodeFactory = function NodeFactory () {};
102529
102530         NodeFactory.prototype.createNode = function createNode (coord) {
102531           return new Node$1(coord, null)
102532         };
102533         NodeFactory.prototype.interfaces_ = function interfaces_ () {
102534           return []
102535         };
102536         NodeFactory.prototype.getClass = function getClass () {
102537           return NodeFactory
102538         };
102539
102540         var PlanarGraph = function PlanarGraph () {
102541           this._edges = new ArrayList();
102542           this._nodes = null;
102543           this._edgeEndList = new ArrayList();
102544           if (arguments.length === 0) {
102545             this._nodes = new NodeMap(new NodeFactory());
102546           } else if (arguments.length === 1) {
102547             var nodeFact = arguments[0];
102548             this._nodes = new NodeMap(nodeFact);
102549           }
102550         };
102551         PlanarGraph.prototype.printEdges = function printEdges (out) {
102552             var this$1 = this;
102553
102554           out.println('Edges:');
102555           for (var i = 0; i < this._edges.size(); i++) {
102556             out.println('edge ' + i + ':');
102557             var e = this$1._edges.get(i);
102558             e.print(out);
102559             e.eiList.print(out);
102560           }
102561         };
102562         PlanarGraph.prototype.find = function find (coord) {
102563           return this._nodes.find(coord)
102564         };
102565         PlanarGraph.prototype.addNode = function addNode () {
102566           if (arguments[0] instanceof Node$1) {
102567             var node = arguments[0];
102568             return this._nodes.addNode(node)
102569           } else if (arguments[0] instanceof Coordinate) {
102570             var coord = arguments[0];
102571             return this._nodes.addNode(coord)
102572           }
102573         };
102574         PlanarGraph.prototype.getNodeIterator = function getNodeIterator () {
102575           return this._nodes.iterator()
102576         };
102577         PlanarGraph.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
102578           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102579             var node = nodeit.next();
102580             node.getEdges().linkResultDirectedEdges();
102581           }
102582         };
102583         PlanarGraph.prototype.debugPrintln = function debugPrintln (o) {
102584           System.out.println(o);
102585         };
102586         PlanarGraph.prototype.isBoundaryNode = function isBoundaryNode (geomIndex, coord) {
102587           var node = this._nodes.find(coord);
102588           if (node === null) { return false }
102589           var label = node.getLabel();
102590           if (label !== null && label.getLocation(geomIndex) === Location.BOUNDARY) { return true }
102591           return false
102592         };
102593         PlanarGraph.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
102594           for (var nodeit = this._nodes.iterator(); nodeit.hasNext();) {
102595             var node = nodeit.next();
102596             node.getEdges().linkAllDirectedEdges();
102597           }
102598         };
102599         PlanarGraph.prototype.matchInSameDirection = function matchInSameDirection (p0, p1, ep0, ep1) {
102600           if (!p0.equals(ep0)) { return false }
102601           if (CGAlgorithms.computeOrientation(p0, p1, ep1) === CGAlgorithms.COLLINEAR && Quadrant.quadrant(p0, p1) === Quadrant.quadrant(ep0, ep1)) { return true }
102602           return false
102603         };
102604         PlanarGraph.prototype.getEdgeEnds = function getEdgeEnds () {
102605           return this._edgeEndList
102606         };
102607         PlanarGraph.prototype.debugPrint = function debugPrint (o) {
102608           System.out.print(o);
102609         };
102610         PlanarGraph.prototype.getEdgeIterator = function getEdgeIterator () {
102611           return this._edges.iterator()
102612         };
102613         PlanarGraph.prototype.findEdgeInSameDirection = function findEdgeInSameDirection (p0, p1) {
102614             var this$1 = this;
102615
102616           for (var i = 0; i < this._edges.size(); i++) {
102617             var e = this$1._edges.get(i);
102618             var eCoord = e.getCoordinates();
102619             if (this$1.matchInSameDirection(p0, p1, eCoord[0], eCoord[1])) { return e }
102620             if (this$1.matchInSameDirection(p0, p1, eCoord[eCoord.length - 1], eCoord[eCoord.length - 2])) { return e }
102621           }
102622           return null
102623         };
102624         PlanarGraph.prototype.insertEdge = function insertEdge (e) {
102625           this._edges.add(e);
102626         };
102627         PlanarGraph.prototype.findEdgeEnd = function findEdgeEnd (e) {
102628           for (var i = this.getEdgeEnds().iterator(); i.hasNext();) {
102629             var ee = i.next();
102630             if (ee.getEdge() === e) { return ee }
102631           }
102632           return null
102633         };
102634         PlanarGraph.prototype.addEdges = function addEdges (edgesToAdd) {
102635             var this$1 = this;
102636
102637           for (var it = edgesToAdd.iterator(); it.hasNext();) {
102638             var e = it.next();
102639             this$1._edges.add(e);
102640             var de1 = new DirectedEdge(e, true);
102641             var de2 = new DirectedEdge(e, false);
102642             de1.setSym(de2);
102643             de2.setSym(de1);
102644             this$1.add(de1);
102645             this$1.add(de2);
102646           }
102647         };
102648         PlanarGraph.prototype.add = function add (e) {
102649           this._nodes.add(e);
102650           this._edgeEndList.add(e);
102651         };
102652         PlanarGraph.prototype.getNodes = function getNodes () {
102653           return this._nodes.values()
102654         };
102655         PlanarGraph.prototype.findEdge = function findEdge (p0, p1) {
102656             var this$1 = this;
102657
102658           for (var i = 0; i < this._edges.size(); i++) {
102659             var e = this$1._edges.get(i);
102660             var eCoord = e.getCoordinates();
102661             if (p0.equals(eCoord[0]) && p1.equals(eCoord[1])) { return e }
102662           }
102663           return null
102664         };
102665         PlanarGraph.prototype.interfaces_ = function interfaces_ () {
102666           return []
102667         };
102668         PlanarGraph.prototype.getClass = function getClass () {
102669           return PlanarGraph
102670         };
102671         PlanarGraph.linkResultDirectedEdges = function linkResultDirectedEdges (nodes) {
102672           for (var nodeit = nodes.iterator(); nodeit.hasNext();) {
102673             var node = nodeit.next();
102674             node.getEdges().linkResultDirectedEdges();
102675           }
102676         };
102677
102678         var PolygonBuilder = function PolygonBuilder () {
102679           this._geometryFactory = null;
102680           this._shellList = new ArrayList();
102681           var geometryFactory = arguments[0];
102682           this._geometryFactory = geometryFactory;
102683         };
102684         PolygonBuilder.prototype.sortShellsAndHoles = function sortShellsAndHoles (edgeRings, shellList, freeHoleList) {
102685           for (var it = edgeRings.iterator(); it.hasNext();) {
102686             var er = it.next();
102687             if (er.isHole()) {
102688               freeHoleList.add(er);
102689             } else {
102690               shellList.add(er);
102691             }
102692           }
102693         };
102694         PolygonBuilder.prototype.computePolygons = function computePolygons (shellList) {
102695             var this$1 = this;
102696
102697           var resultPolyList = new ArrayList();
102698           for (var it = shellList.iterator(); it.hasNext();) {
102699             var er = it.next();
102700             var poly = er.toPolygon(this$1._geometryFactory);
102701             resultPolyList.add(poly);
102702           }
102703           return resultPolyList
102704         };
102705         PolygonBuilder.prototype.placeFreeHoles = function placeFreeHoles (shellList, freeHoleList) {
102706             var this$1 = this;
102707
102708           for (var it = freeHoleList.iterator(); it.hasNext();) {
102709             var hole = it.next();
102710             if (hole.getShell() === null) {
102711               var shell = this$1.findEdgeRingContaining(hole, shellList);
102712               if (shell === null) { throw new TopologyException('unable to assign hole to a shell', hole.getCoordinate(0)) }
102713               hole.setShell(shell);
102714             }
102715           }
102716         };
102717         PolygonBuilder.prototype.buildMinimalEdgeRings = function buildMinimalEdgeRings (maxEdgeRings, shellList, freeHoleList) {
102718             var this$1 = this;
102719
102720           var edgeRings = new ArrayList();
102721           for (var it = maxEdgeRings.iterator(); it.hasNext();) {
102722             var er = it.next();
102723             if (er.getMaxNodeDegree() > 2) {
102724               er.linkDirectedEdgesForMinimalEdgeRings();
102725               var minEdgeRings = er.buildMinimalRings();
102726               var shell = this$1.findShell(minEdgeRings);
102727               if (shell !== null) {
102728                 this$1.placePolygonHoles(shell, minEdgeRings);
102729                 shellList.add(shell);
102730               } else {
102731                 freeHoleList.addAll(minEdgeRings);
102732               }
102733             } else {
102734               edgeRings.add(er);
102735             }
102736           }
102737           return edgeRings
102738         };
102739         PolygonBuilder.prototype.containsPoint = function containsPoint (p) {
102740           for (var it = this._shellList.iterator(); it.hasNext();) {
102741             var er = it.next();
102742             if (er.containsPoint(p)) { return true }
102743           }
102744           return false
102745         };
102746         PolygonBuilder.prototype.buildMaximalEdgeRings = function buildMaximalEdgeRings (dirEdges) {
102747             var this$1 = this;
102748
102749           var maxEdgeRings = new ArrayList();
102750           for (var it = dirEdges.iterator(); it.hasNext();) {
102751             var de = it.next();
102752             if (de.isInResult() && de.getLabel().isArea()) {
102753               if (de.getEdgeRing() === null) {
102754                 var er = new MaximalEdgeRing(de, this$1._geometryFactory);
102755                 maxEdgeRings.add(er);
102756                 er.setInResult();
102757               }
102758             }
102759           }
102760           return maxEdgeRings
102761         };
102762         PolygonBuilder.prototype.placePolygonHoles = function placePolygonHoles (shell, minEdgeRings) {
102763           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102764             var er = it.next();
102765             if (er.isHole()) {
102766               er.setShell(shell);
102767             }
102768           }
102769         };
102770         PolygonBuilder.prototype.getPolygons = function getPolygons () {
102771           var resultPolyList = this.computePolygons(this._shellList);
102772           return resultPolyList
102773         };
102774         PolygonBuilder.prototype.findEdgeRingContaining = function findEdgeRingContaining (testEr, shellList) {
102775           var testRing = testEr.getLinearRing();
102776           var testEnv = testRing.getEnvelopeInternal();
102777           var testPt = testRing.getCoordinateN(0);
102778           var minShell = null;
102779           var minEnv = null;
102780           for (var it = shellList.iterator(); it.hasNext();) {
102781             var tryShell = it.next();
102782             var tryRing = tryShell.getLinearRing();
102783             var tryEnv = tryRing.getEnvelopeInternal();
102784             if (minShell !== null) { minEnv = minShell.getLinearRing().getEnvelopeInternal(); }
102785             var isContained = false;
102786             if (tryEnv.contains(testEnv) && CGAlgorithms.isPointInRing(testPt, tryRing.getCoordinates())) { isContained = true; }
102787             if (isContained) {
102788               if (minShell === null || minEnv.contains(tryEnv)) {
102789                 minShell = tryShell;
102790               }
102791             }
102792           }
102793           return minShell
102794         };
102795         PolygonBuilder.prototype.findShell = function findShell (minEdgeRings) {
102796           var shellCount = 0;
102797           var shell = null;
102798           for (var it = minEdgeRings.iterator(); it.hasNext();) {
102799             var er = it.next();
102800             if (!er.isHole()) {
102801               shell = er;
102802               shellCount++;
102803             }
102804           }
102805           Assert.isTrue(shellCount <= 1, 'found two shells in MinimalEdgeRing list');
102806           return shell
102807         };
102808         PolygonBuilder.prototype.add = function add () {
102809           if (arguments.length === 1) {
102810             var graph = arguments[0];
102811             this.add(graph.getEdgeEnds(), graph.getNodes());
102812           } else if (arguments.length === 2) {
102813             var dirEdges = arguments[0];
102814             var nodes = arguments[1];
102815             PlanarGraph.linkResultDirectedEdges(nodes);
102816             var maxEdgeRings = this.buildMaximalEdgeRings(dirEdges);
102817             var freeHoleList = new ArrayList();
102818             var edgeRings = this.buildMinimalEdgeRings(maxEdgeRings, this._shellList, freeHoleList);
102819             this.sortShellsAndHoles(edgeRings, this._shellList, freeHoleList);
102820             this.placeFreeHoles(this._shellList, freeHoleList);
102821           }
102822         };
102823         PolygonBuilder.prototype.interfaces_ = function interfaces_ () {
102824           return []
102825         };
102826         PolygonBuilder.prototype.getClass = function getClass () {
102827           return PolygonBuilder
102828         };
102829
102830         var Boundable = function Boundable () {};
102831
102832         Boundable.prototype.getBounds = function getBounds () {};
102833         Boundable.prototype.interfaces_ = function interfaces_ () {
102834           return []
102835         };
102836         Boundable.prototype.getClass = function getClass () {
102837           return Boundable
102838         };
102839
102840         var ItemBoundable = function ItemBoundable () {
102841           this._bounds = null;
102842           this._item = null;
102843           var bounds = arguments[0];
102844           var item = arguments[1];
102845           this._bounds = bounds;
102846           this._item = item;
102847         };
102848         ItemBoundable.prototype.getItem = function getItem () {
102849           return this._item
102850         };
102851         ItemBoundable.prototype.getBounds = function getBounds () {
102852           return this._bounds
102853         };
102854         ItemBoundable.prototype.interfaces_ = function interfaces_ () {
102855           return [Boundable, Serializable]
102856         };
102857         ItemBoundable.prototype.getClass = function getClass () {
102858           return ItemBoundable
102859         };
102860
102861         var PriorityQueue = function PriorityQueue () {
102862           this._size = null;
102863           this._items = null;
102864           this._size = 0;
102865           this._items = new ArrayList();
102866           this._items.add(null);
102867         };
102868         PriorityQueue.prototype.poll = function poll () {
102869           if (this.isEmpty()) { return null }
102870           var minItem = this._items.get(1);
102871           this._items.set(1, this._items.get(this._size));
102872           this._size -= 1;
102873           this.reorder(1);
102874           return minItem
102875         };
102876         PriorityQueue.prototype.size = function size () {
102877           return this._size
102878         };
102879         PriorityQueue.prototype.reorder = function reorder (hole) {
102880             var this$1 = this;
102881
102882           var child = null;
102883           var tmp = this._items.get(hole);
102884           for (; hole * 2 <= this._size; hole = child) {
102885             child = hole * 2;
102886             if (child !== this$1._size && this$1._items.get(child + 1).compareTo(this$1._items.get(child)) < 0) { child++; }
102887             if (this$1._items.get(child).compareTo(tmp) < 0) { this$1._items.set(hole, this$1._items.get(child)); } else { break }
102888           }
102889           this._items.set(hole, tmp);
102890         };
102891         PriorityQueue.prototype.clear = function clear () {
102892           this._size = 0;
102893           this._items.clear();
102894         };
102895         PriorityQueue.prototype.isEmpty = function isEmpty () {
102896           return this._size === 0
102897         };
102898         PriorityQueue.prototype.add = function add (x) {
102899             var this$1 = this;
102900
102901           this._items.add(null);
102902           this._size += 1;
102903           var hole = this._size;
102904           this._items.set(0, x);
102905           for (; x.compareTo(this._items.get(Math.trunc(hole / 2))) < 0; hole /= 2) {
102906             this$1._items.set(hole, this$1._items.get(Math.trunc(hole / 2)));
102907           }
102908           this._items.set(hole, x);
102909         };
102910         PriorityQueue.prototype.interfaces_ = function interfaces_ () {
102911           return []
102912         };
102913         PriorityQueue.prototype.getClass = function getClass () {
102914           return PriorityQueue
102915         };
102916
102917         var ItemVisitor = function ItemVisitor () {};
102918
102919         ItemVisitor.prototype.visitItem = function visitItem (item) {};
102920         ItemVisitor.prototype.interfaces_ = function interfaces_ () {
102921           return []
102922         };
102923         ItemVisitor.prototype.getClass = function getClass () {
102924           return ItemVisitor
102925         };
102926
102927         var SpatialIndex = function SpatialIndex () {};
102928
102929         SpatialIndex.prototype.insert = function insert (itemEnv, item) {};
102930         SpatialIndex.prototype.remove = function remove (itemEnv, item) {};
102931         SpatialIndex.prototype.query = function query () {
102932           // if (arguments.length === 1) {
102933           // const searchEnv = arguments[0]
102934           // } else if (arguments.length === 2) {
102935           // const searchEnv = arguments[0]
102936           // const visitor = arguments[1]
102937           // }
102938         };
102939         SpatialIndex.prototype.interfaces_ = function interfaces_ () {
102940           return []
102941         };
102942         SpatialIndex.prototype.getClass = function getClass () {
102943           return SpatialIndex
102944         };
102945
102946         var AbstractNode = function AbstractNode () {
102947           this._childBoundables = new ArrayList();
102948           this._bounds = null;
102949           this._level = null;
102950           if (arguments.length === 0) ; else if (arguments.length === 1) {
102951             var level = arguments[0];
102952             this._level = level;
102953           }
102954         };
102955
102956         var staticAccessors$22 = { serialVersionUID: { configurable: true } };
102957         AbstractNode.prototype.getLevel = function getLevel () {
102958           return this._level
102959         };
102960         AbstractNode.prototype.size = function size () {
102961           return this._childBoundables.size()
102962         };
102963         AbstractNode.prototype.getChildBoundables = function getChildBoundables () {
102964           return this._childBoundables
102965         };
102966         AbstractNode.prototype.addChildBoundable = function addChildBoundable (childBoundable) {
102967           Assert.isTrue(this._bounds === null);
102968           this._childBoundables.add(childBoundable);
102969         };
102970         AbstractNode.prototype.isEmpty = function isEmpty () {
102971           return this._childBoundables.isEmpty()
102972         };
102973         AbstractNode.prototype.getBounds = function getBounds () {
102974           if (this._bounds === null) {
102975             this._bounds = this.computeBounds();
102976           }
102977           return this._bounds
102978         };
102979         AbstractNode.prototype.interfaces_ = function interfaces_ () {
102980           return [Boundable, Serializable]
102981         };
102982         AbstractNode.prototype.getClass = function getClass () {
102983           return AbstractNode
102984         };
102985         staticAccessors$22.serialVersionUID.get = function () { return 6493722185909573708 };
102986
102987         Object.defineProperties( AbstractNode, staticAccessors$22 );
102988
102989         var Collections = function Collections () {};
102990
102991         Collections.reverseOrder = function reverseOrder () {
102992           return {
102993             compare: function compare (a, b) {
102994               return b.compareTo(a)
102995             }
102996           }
102997         };
102998         Collections.min = function min (l) {
102999           Collections.sort(l);
103000           return l.get(0)
103001         };
103002         Collections.sort = function sort (l, c) {
103003           var a = l.toArray();
103004           if (c) {
103005             Arrays.sort(a, c);
103006           } else {
103007             Arrays.sort(a);
103008           }
103009           var i = l.iterator();
103010           for (var pos = 0, alen = a.length; pos < alen; pos++) {
103011             i.next();
103012             i.set(a[pos]);
103013           }
103014         };
103015         Collections.singletonList = function singletonList (o) {
103016           var arrayList = new ArrayList();
103017           arrayList.add(o);
103018           return arrayList
103019         };
103020
103021         var BoundablePair = function BoundablePair () {
103022           this._boundable1 = null;
103023           this._boundable2 = null;
103024           this._distance = null;
103025           this._itemDistance = null;
103026           var boundable1 = arguments[0];
103027           var boundable2 = arguments[1];
103028           var itemDistance = arguments[2];
103029           this._boundable1 = boundable1;
103030           this._boundable2 = boundable2;
103031           this._itemDistance = itemDistance;
103032           this._distance = this.distance();
103033         };
103034         BoundablePair.prototype.expandToQueue = function expandToQueue (priQ, minDistance) {
103035           var isComp1 = BoundablePair.isComposite(this._boundable1);
103036           var isComp2 = BoundablePair.isComposite(this._boundable2);
103037           if (isComp1 && isComp2) {
103038             if (BoundablePair.area(this._boundable1) > BoundablePair.area(this._boundable2)) {
103039               this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103040               return null
103041             } else {
103042               this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103043               return null
103044             }
103045           } else if (isComp1) {
103046             this.expand(this._boundable1, this._boundable2, priQ, minDistance);
103047             return null
103048           } else if (isComp2) {
103049             this.expand(this._boundable2, this._boundable1, priQ, minDistance);
103050             return null
103051           }
103052           throw new IllegalArgumentException('neither boundable is composite')
103053         };
103054         BoundablePair.prototype.isLeaves = function isLeaves () {
103055           return !(BoundablePair.isComposite(this._boundable1) || BoundablePair.isComposite(this._boundable2))
103056         };
103057         BoundablePair.prototype.compareTo = function compareTo (o) {
103058           var nd = o;
103059           if (this._distance < nd._distance) { return -1 }
103060           if (this._distance > nd._distance) { return 1 }
103061           return 0
103062         };
103063         BoundablePair.prototype.expand = function expand (bndComposite, bndOther, priQ, minDistance) {
103064             var this$1 = this;
103065
103066           var children = bndComposite.getChildBoundables();
103067           for (var i = children.iterator(); i.hasNext();) {
103068             var child = i.next();
103069             var bp = new BoundablePair(child, bndOther, this$1._itemDistance);
103070             if (bp.getDistance() < minDistance) {
103071               priQ.add(bp);
103072             }
103073           }
103074         };
103075         BoundablePair.prototype.getBoundable = function getBoundable (i) {
103076           if (i === 0) { return this._boundable1 }
103077           return this._boundable2
103078         };
103079         BoundablePair.prototype.getDistance = function getDistance () {
103080           return this._distance
103081         };
103082         BoundablePair.prototype.distance = function distance () {
103083           if (this.isLeaves()) {
103084             return this._itemDistance.distance(this._boundable1, this._boundable2)
103085           }
103086           return this._boundable1.getBounds().distance(this._boundable2.getBounds())
103087         };
103088         BoundablePair.prototype.interfaces_ = function interfaces_ () {
103089           return [Comparable]
103090         };
103091         BoundablePair.prototype.getClass = function getClass () {
103092           return BoundablePair
103093         };
103094         BoundablePair.area = function area (b) {
103095           return b.getBounds().getArea()
103096         };
103097         BoundablePair.isComposite = function isComposite (item) {
103098           return item instanceof AbstractNode
103099         };
103100
103101         var AbstractSTRtree = function AbstractSTRtree () {
103102           this._root = null;
103103           this._built = false;
103104           this._itemBoundables = new ArrayList();
103105           this._nodeCapacity = null;
103106           if (arguments.length === 0) {
103107             var nodeCapacity = AbstractSTRtree.DEFAULT_NODE_CAPACITY;
103108             this._nodeCapacity = nodeCapacity;
103109           } else if (arguments.length === 1) {
103110             var nodeCapacity$1 = arguments[0];
103111             Assert.isTrue(nodeCapacity$1 > 1, 'Node capacity must be greater than 1');
103112             this._nodeCapacity = nodeCapacity$1;
103113           }
103114         };
103115
103116         var staticAccessors$23 = { IntersectsOp: { configurable: true },serialVersionUID: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103117         AbstractSTRtree.prototype.getNodeCapacity = function getNodeCapacity () {
103118           return this._nodeCapacity
103119         };
103120         AbstractSTRtree.prototype.lastNode = function lastNode (nodes) {
103121           return nodes.get(nodes.size() - 1)
103122         };
103123         AbstractSTRtree.prototype.size = function size () {
103124             var this$1 = this;
103125
103126           if (arguments.length === 0) {
103127             if (this.isEmpty()) {
103128               return 0
103129             }
103130             this.build();
103131             return this.size(this._root)
103132           } else if (arguments.length === 1) {
103133             var node = arguments[0];
103134             var size = 0;
103135             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103136               var childBoundable = i.next();
103137               if (childBoundable instanceof AbstractNode) {
103138                 size += this$1.size(childBoundable);
103139               } else if (childBoundable instanceof ItemBoundable) {
103140                 size += 1;
103141               }
103142             }
103143             return size
103144           }
103145         };
103146         AbstractSTRtree.prototype.removeItem = function removeItem (node, item) {
103147           var childToRemove = null;
103148           for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103149             var childBoundable = i.next();
103150             if (childBoundable instanceof ItemBoundable) {
103151               if (childBoundable.getItem() === item) { childToRemove = childBoundable; }
103152             }
103153           }
103154           if (childToRemove !== null) {
103155             node.getChildBoundables().remove(childToRemove);
103156             return true
103157           }
103158           return false
103159         };
103160         AbstractSTRtree.prototype.itemsTree = function itemsTree () {
103161             var this$1 = this;
103162
103163           if (arguments.length === 0) {
103164             this.build();
103165             var valuesTree = this.itemsTree(this._root);
103166             if (valuesTree === null) { return new ArrayList() }
103167             return valuesTree
103168           } else if (arguments.length === 1) {
103169             var node = arguments[0];
103170             var valuesTreeForNode = new ArrayList();
103171             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103172               var childBoundable = i.next();
103173               if (childBoundable instanceof AbstractNode) {
103174                 var valuesTreeForChild = this$1.itemsTree(childBoundable);
103175                 if (valuesTreeForChild !== null) { valuesTreeForNode.add(valuesTreeForChild); }
103176               } else if (childBoundable instanceof ItemBoundable) {
103177                 valuesTreeForNode.add(childBoundable.getItem());
103178               } else {
103179                 Assert.shouldNeverReachHere();
103180               }
103181             }
103182             if (valuesTreeForNode.size() <= 0) { return null }
103183             return valuesTreeForNode
103184           }
103185         };
103186         AbstractSTRtree.prototype.insert = function insert (bounds, item) {
103187           Assert.isTrue(!this._built, 'Cannot insert items into an STR packed R-tree after it has been built.');
103188           this._itemBoundables.add(new ItemBoundable(bounds, item));
103189         };
103190         AbstractSTRtree.prototype.boundablesAtLevel = function boundablesAtLevel () {
103191             var this$1 = this;
103192
103193           if (arguments.length === 1) {
103194             var level = arguments[0];
103195             var boundables = new ArrayList();
103196             this.boundablesAtLevel(level, this._root, boundables);
103197             return boundables
103198           } else if (arguments.length === 3) {
103199             var level$1 = arguments[0];
103200             var top = arguments[1];
103201             var boundables$1 = arguments[2];
103202             Assert.isTrue(level$1 > -2);
103203             if (top.getLevel() === level$1) {
103204               boundables$1.add(top);
103205               return null
103206             }
103207             for (var i = top.getChildBoundables().iterator(); i.hasNext();) {
103208               var boundable = i.next();
103209               if (boundable instanceof AbstractNode) {
103210                 this$1.boundablesAtLevel(level$1, boundable, boundables$1);
103211               } else {
103212                 Assert.isTrue(boundable instanceof ItemBoundable);
103213                 if (level$1 === -1) {
103214                   boundables$1.add(boundable);
103215                 }
103216               }
103217             }
103218             return null
103219           }
103220         };
103221         AbstractSTRtree.prototype.query = function query () {
103222             var this$1 = this;
103223
103224           if (arguments.length === 1) {
103225             var searchBounds = arguments[0];
103226             this.build();
103227             var matches = new ArrayList();
103228             if (this.isEmpty()) {
103229               return matches
103230             }
103231             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103232               this.query(searchBounds, this._root, matches);
103233             }
103234             return matches
103235           } else if (arguments.length === 2) {
103236             var searchBounds$1 = arguments[0];
103237             var visitor = arguments[1];
103238             this.build();
103239             if (this.isEmpty()) {
103240               return null
103241             }
103242             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds$1)) {
103243               this.query(searchBounds$1, this._root, visitor);
103244             }
103245           } else if (arguments.length === 3) {
103246             if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103247               var searchBounds$2 = arguments[0];
103248               var node = arguments[1];
103249               var visitor$1 = arguments[2];
103250               var childBoundables = node.getChildBoundables();
103251               for (var i = 0; i < childBoundables.size(); i++) {
103252                 var childBoundable = childBoundables.get(i);
103253                 if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$2)) {
103254                   continue
103255                 }
103256                 if (childBoundable instanceof AbstractNode) {
103257                   this$1.query(searchBounds$2, childBoundable, visitor$1);
103258                 } else if (childBoundable instanceof ItemBoundable) {
103259                   visitor$1.visitItem(childBoundable.getItem());
103260                 } else {
103261                   Assert.shouldNeverReachHere();
103262                 }
103263               }
103264             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103265               var searchBounds$3 = arguments[0];
103266               var node$1 = arguments[1];
103267               var matches$1 = arguments[2];
103268               var childBoundables$1 = node$1.getChildBoundables();
103269               for (var i$1 = 0; i$1 < childBoundables$1.size(); i$1++) {
103270                 var childBoundable$1 = childBoundables$1.get(i$1);
103271                 if (!this$1.getIntersectsOp().intersects(childBoundable$1.getBounds(), searchBounds$3)) {
103272                   continue
103273                 }
103274                 if (childBoundable$1 instanceof AbstractNode) {
103275                   this$1.query(searchBounds$3, childBoundable$1, matches$1);
103276                 } else if (childBoundable$1 instanceof ItemBoundable) {
103277                   matches$1.add(childBoundable$1.getItem());
103278                 } else {
103279                   Assert.shouldNeverReachHere();
103280                 }
103281               }
103282             }
103283           }
103284         };
103285         AbstractSTRtree.prototype.build = function build () {
103286           if (this._built) { return null }
103287           this._root = this._itemBoundables.isEmpty() ? this.createNode(0) : this.createHigherLevels(this._itemBoundables, -1);
103288           this._itemBoundables = null;
103289           this._built = true;
103290         };
103291         AbstractSTRtree.prototype.getRoot = function getRoot () {
103292           this.build();
103293           return this._root
103294         };
103295         AbstractSTRtree.prototype.remove = function remove () {
103296             var this$1 = this;
103297
103298           if (arguments.length === 2) {
103299             var searchBounds = arguments[0];
103300             var item = arguments[1];
103301             this.build();
103302             if (this.getIntersectsOp().intersects(this._root.getBounds(), searchBounds)) {
103303               return this.remove(searchBounds, this._root, item)
103304             }
103305             return false
103306           } else if (arguments.length === 3) {
103307             var searchBounds$1 = arguments[0];
103308             var node = arguments[1];
103309             var item$1 = arguments[2];
103310             var found = this.removeItem(node, item$1);
103311             if (found) { return true }
103312             var childToPrune = null;
103313             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103314               var childBoundable = i.next();
103315               if (!this$1.getIntersectsOp().intersects(childBoundable.getBounds(), searchBounds$1)) {
103316                 continue
103317               }
103318               if (childBoundable instanceof AbstractNode) {
103319                 found = this$1.remove(searchBounds$1, childBoundable, item$1);
103320                 if (found) {
103321                   childToPrune = childBoundable;
103322                   break
103323                 }
103324               }
103325             }
103326             if (childToPrune !== null) {
103327               if (childToPrune.getChildBoundables().isEmpty()) {
103328                 node.getChildBoundables().remove(childToPrune);
103329               }
103330             }
103331             return found
103332           }
103333         };
103334         AbstractSTRtree.prototype.createHigherLevels = function createHigherLevels (boundablesOfALevel, level) {
103335           Assert.isTrue(!boundablesOfALevel.isEmpty());
103336           var parentBoundables = this.createParentBoundables(boundablesOfALevel, level + 1);
103337           if (parentBoundables.size() === 1) {
103338             return parentBoundables.get(0)
103339           }
103340           return this.createHigherLevels(parentBoundables, level + 1)
103341         };
103342         AbstractSTRtree.prototype.depth = function depth () {
103343             var this$1 = this;
103344
103345           if (arguments.length === 0) {
103346             if (this.isEmpty()) {
103347               return 0
103348             }
103349             this.build();
103350             return this.depth(this._root)
103351           } else if (arguments.length === 1) {
103352             var node = arguments[0];
103353             var maxChildDepth = 0;
103354             for (var i = node.getChildBoundables().iterator(); i.hasNext();) {
103355               var childBoundable = i.next();
103356               if (childBoundable instanceof AbstractNode) {
103357                 var childDepth = this$1.depth(childBoundable);
103358                 if (childDepth > maxChildDepth) { maxChildDepth = childDepth; }
103359               }
103360             }
103361             return maxChildDepth + 1
103362           }
103363         };
103364         AbstractSTRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103365             var this$1 = this;
103366
103367           Assert.isTrue(!childBoundables.isEmpty());
103368           var parentBoundables = new ArrayList();
103369           parentBoundables.add(this.createNode(newLevel));
103370           var sortedChildBoundables = new ArrayList(childBoundables);
103371           Collections.sort(sortedChildBoundables, this.getComparator());
103372           for (var i = sortedChildBoundables.iterator(); i.hasNext();) {
103373             var childBoundable = i.next();
103374             if (this$1.lastNode(parentBoundables).getChildBoundables().size() === this$1.getNodeCapacity()) {
103375               parentBoundables.add(this$1.createNode(newLevel));
103376             }
103377             this$1.lastNode(parentBoundables).addChildBoundable(childBoundable);
103378           }
103379           return parentBoundables
103380         };
103381         AbstractSTRtree.prototype.isEmpty = function isEmpty () {
103382           if (!this._built) { return this._itemBoundables.isEmpty() }
103383           return this._root.isEmpty()
103384         };
103385         AbstractSTRtree.prototype.interfaces_ = function interfaces_ () {
103386           return [Serializable]
103387         };
103388         AbstractSTRtree.prototype.getClass = function getClass () {
103389           return AbstractSTRtree
103390         };
103391         AbstractSTRtree.compareDoubles = function compareDoubles (a, b) {
103392           return a > b ? 1 : a < b ? -1 : 0
103393         };
103394         staticAccessors$23.IntersectsOp.get = function () { return IntersectsOp };
103395         staticAccessors$23.serialVersionUID.get = function () { return -3886435814360241337 };
103396         staticAccessors$23.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103397
103398         Object.defineProperties( AbstractSTRtree, staticAccessors$23 );
103399
103400         var IntersectsOp = function IntersectsOp () {};
103401
103402         var ItemDistance = function ItemDistance () {};
103403
103404         ItemDistance.prototype.distance = function distance (item1, item2) {};
103405         ItemDistance.prototype.interfaces_ = function interfaces_ () {
103406           return []
103407         };
103408         ItemDistance.prototype.getClass = function getClass () {
103409           return ItemDistance
103410         };
103411
103412         var STRtree = (function (AbstractSTRtree$$1) {
103413           function STRtree (nodeCapacity) {
103414             nodeCapacity = nodeCapacity || STRtree.DEFAULT_NODE_CAPACITY;
103415             AbstractSTRtree$$1.call(this, nodeCapacity);
103416           }
103417
103418           if ( AbstractSTRtree$$1 ) { STRtree.__proto__ = AbstractSTRtree$$1; }
103419           STRtree.prototype = Object.create( AbstractSTRtree$$1 && AbstractSTRtree$$1.prototype );
103420           STRtree.prototype.constructor = STRtree;
103421
103422           var staticAccessors = { STRtreeNode: { configurable: true },serialVersionUID: { configurable: true },xComparator: { configurable: true },yComparator: { configurable: true },intersectsOp: { configurable: true },DEFAULT_NODE_CAPACITY: { configurable: true } };
103423           STRtree.prototype.createParentBoundablesFromVerticalSlices = function createParentBoundablesFromVerticalSlices (verticalSlices, newLevel) {
103424             var this$1 = this;
103425
103426             Assert.isTrue(verticalSlices.length > 0);
103427             var parentBoundables = new ArrayList();
103428             for (var i = 0; i < verticalSlices.length; i++) {
103429               parentBoundables.addAll(this$1.createParentBoundablesFromVerticalSlice(verticalSlices[i], newLevel));
103430             }
103431             return parentBoundables
103432           };
103433           STRtree.prototype.createNode = function createNode (level) {
103434             return new STRtreeNode(level)
103435           };
103436           STRtree.prototype.size = function size () {
103437             if (arguments.length === 0) {
103438               return AbstractSTRtree$$1.prototype.size.call(this)
103439             } else { return AbstractSTRtree$$1.prototype.size.apply(this, arguments) }
103440           };
103441           STRtree.prototype.insert = function insert () {
103442             if (arguments.length === 2) {
103443               var itemEnv = arguments[0];
103444               var item = arguments[1];
103445               if (itemEnv.isNull()) {
103446                 return null
103447               }
103448               AbstractSTRtree$$1.prototype.insert.call(this, itemEnv, item);
103449             } else { return AbstractSTRtree$$1.prototype.insert.apply(this, arguments) }
103450           };
103451           STRtree.prototype.getIntersectsOp = function getIntersectsOp () {
103452             return STRtree.intersectsOp
103453           };
103454           STRtree.prototype.verticalSlices = function verticalSlices (childBoundables, sliceCount) {
103455             var sliceCapacity = Math.trunc(Math.ceil(childBoundables.size() / sliceCount));
103456             var slices = new Array(sliceCount).fill(null);
103457             var i = childBoundables.iterator();
103458             for (var j = 0; j < sliceCount; j++) {
103459               slices[j] = new ArrayList();
103460               var boundablesAddedToSlice = 0;
103461               while (i.hasNext() && boundablesAddedToSlice < sliceCapacity) {
103462                 var childBoundable = i.next();
103463                 slices[j].add(childBoundable);
103464                 boundablesAddedToSlice++;
103465               }
103466             }
103467             return slices
103468           };
103469           STRtree.prototype.query = function query () {
103470             if (arguments.length === 1) {
103471               var searchEnv = arguments[0];
103472               return AbstractSTRtree$$1.prototype.query.call(this, searchEnv)
103473             } else if (arguments.length === 2) {
103474               var searchEnv$1 = arguments[0];
103475               var visitor = arguments[1];
103476               AbstractSTRtree$$1.prototype.query.call(this, searchEnv$1, visitor);
103477             } else if (arguments.length === 3) {
103478               if (hasInterface(arguments[2], ItemVisitor) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103479                 var searchBounds = arguments[0];
103480                 var node = arguments[1];
103481                 var visitor$1 = arguments[2];
103482                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds, node, visitor$1);
103483               } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Object && arguments[1] instanceof AbstractNode)) {
103484                 var searchBounds$1 = arguments[0];
103485                 var node$1 = arguments[1];
103486                 var matches = arguments[2];
103487                 AbstractSTRtree$$1.prototype.query.call(this, searchBounds$1, node$1, matches);
103488               }
103489             }
103490           };
103491           STRtree.prototype.getComparator = function getComparator () {
103492             return STRtree.yComparator
103493           };
103494           STRtree.prototype.createParentBoundablesFromVerticalSlice = function createParentBoundablesFromVerticalSlice (childBoundables, newLevel) {
103495             return AbstractSTRtree$$1.prototype.createParentBoundables.call(this, childBoundables, newLevel)
103496           };
103497           STRtree.prototype.remove = function remove () {
103498             if (arguments.length === 2) {
103499               var itemEnv = arguments[0];
103500               var item = arguments[1];
103501               return AbstractSTRtree$$1.prototype.remove.call(this, itemEnv, item)
103502             } else { return AbstractSTRtree$$1.prototype.remove.apply(this, arguments) }
103503           };
103504           STRtree.prototype.depth = function depth () {
103505             if (arguments.length === 0) {
103506               return AbstractSTRtree$$1.prototype.depth.call(this)
103507             } else { return AbstractSTRtree$$1.prototype.depth.apply(this, arguments) }
103508           };
103509           STRtree.prototype.createParentBoundables = function createParentBoundables (childBoundables, newLevel) {
103510             Assert.isTrue(!childBoundables.isEmpty());
103511             var minLeafCount = Math.trunc(Math.ceil(childBoundables.size() / this.getNodeCapacity()));
103512             var sortedChildBoundables = new ArrayList(childBoundables);
103513             Collections.sort(sortedChildBoundables, STRtree.xComparator);
103514             var verticalSlices = this.verticalSlices(sortedChildBoundables, Math.trunc(Math.ceil(Math.sqrt(minLeafCount))));
103515             return this.createParentBoundablesFromVerticalSlices(verticalSlices, newLevel)
103516           };
103517           STRtree.prototype.nearestNeighbour = function nearestNeighbour () {
103518             if (arguments.length === 1) {
103519               if (hasInterface(arguments[0], ItemDistance)) {
103520                 var itemDist = arguments[0];
103521                 var bp = new BoundablePair(this.getRoot(), this.getRoot(), itemDist);
103522                 return this.nearestNeighbour(bp)
103523               } else if (arguments[0] instanceof BoundablePair) {
103524                 var initBndPair = arguments[0];
103525                 return this.nearestNeighbour(initBndPair, Double.POSITIVE_INFINITY)
103526               }
103527             } else if (arguments.length === 2) {
103528               if (arguments[0] instanceof STRtree && hasInterface(arguments[1], ItemDistance)) {
103529                 var tree = arguments[0];
103530                 var itemDist$1 = arguments[1];
103531                 var bp$1 = new BoundablePair(this.getRoot(), tree.getRoot(), itemDist$1);
103532                 return this.nearestNeighbour(bp$1)
103533               } else if (arguments[0] instanceof BoundablePair && typeof arguments[1] === 'number') {
103534                 var initBndPair$1 = arguments[0];
103535                 var maxDistance = arguments[1];
103536                 var distanceLowerBound = maxDistance;
103537                 var minPair = null;
103538                 var priQ = new PriorityQueue();
103539                 priQ.add(initBndPair$1);
103540                 while (!priQ.isEmpty() && distanceLowerBound > 0.0) {
103541                   var bndPair = priQ.poll();
103542                   var currentDistance = bndPair.getDistance();
103543                   if (currentDistance >= distanceLowerBound) { break }
103544                   if (bndPair.isLeaves()) {
103545                     distanceLowerBound = currentDistance;
103546                     minPair = bndPair;
103547                   } else {
103548                     bndPair.expandToQueue(priQ, distanceLowerBound);
103549                   }
103550                 }
103551                 return [minPair.getBoundable(0).getItem(), minPair.getBoundable(1).getItem()]
103552               }
103553             } else if (arguments.length === 3) {
103554               var env = arguments[0];
103555               var item = arguments[1];
103556               var itemDist$2 = arguments[2];
103557               var bnd = new ItemBoundable(env, item);
103558               var bp$2 = new BoundablePair(this.getRoot(), bnd, itemDist$2);
103559               return this.nearestNeighbour(bp$2)[0]
103560             }
103561           };
103562           STRtree.prototype.interfaces_ = function interfaces_ () {
103563             return [SpatialIndex, Serializable]
103564           };
103565           STRtree.prototype.getClass = function getClass () {
103566             return STRtree
103567           };
103568           STRtree.centreX = function centreX (e) {
103569             return STRtree.avg(e.getMinX(), e.getMaxX())
103570           };
103571           STRtree.avg = function avg (a, b) {
103572             return (a + b) / 2
103573           };
103574           STRtree.centreY = function centreY (e) {
103575             return STRtree.avg(e.getMinY(), e.getMaxY())
103576           };
103577           staticAccessors.STRtreeNode.get = function () { return STRtreeNode };
103578           staticAccessors.serialVersionUID.get = function () { return 259274702368956900 };
103579           staticAccessors.xComparator.get = function () {
103580             return {
103581               interfaces_: function () {
103582                 return [Comparator]
103583               },
103584               compare: function (o1, o2) {
103585                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreX(o1.getBounds()), STRtree.centreX(o2.getBounds()))
103586               }
103587             }
103588           };
103589           staticAccessors.yComparator.get = function () {
103590             return {
103591               interfaces_: function () {
103592                 return [Comparator]
103593               },
103594               compare: function (o1, o2) {
103595                 return AbstractSTRtree$$1.compareDoubles(STRtree.centreY(o1.getBounds()), STRtree.centreY(o2.getBounds()))
103596               }
103597             }
103598           };
103599           staticAccessors.intersectsOp.get = function () {
103600             return {
103601               interfaces_: function () {
103602                 return [AbstractSTRtree$$1.IntersectsOp]
103603               },
103604               intersects: function (aBounds, bBounds) {
103605                 return aBounds.intersects(bBounds)
103606               }
103607             }
103608           };
103609           staticAccessors.DEFAULT_NODE_CAPACITY.get = function () { return 10 };
103610
103611           Object.defineProperties( STRtree, staticAccessors );
103612
103613           return STRtree;
103614         }(AbstractSTRtree));
103615
103616         var STRtreeNode = (function (AbstractNode$$1) {
103617           function STRtreeNode () {
103618             var level = arguments[0];
103619             AbstractNode$$1.call(this, level);
103620           }
103621
103622           if ( AbstractNode$$1 ) { STRtreeNode.__proto__ = AbstractNode$$1; }
103623           STRtreeNode.prototype = Object.create( AbstractNode$$1 && AbstractNode$$1.prototype );
103624           STRtreeNode.prototype.constructor = STRtreeNode;
103625           STRtreeNode.prototype.computeBounds = function computeBounds () {
103626             var bounds = null;
103627             for (var i = this.getChildBoundables().iterator(); i.hasNext();) {
103628               var childBoundable = i.next();
103629               if (bounds === null) {
103630                 bounds = new Envelope(childBoundable.getBounds());
103631               } else {
103632                 bounds.expandToInclude(childBoundable.getBounds());
103633               }
103634             }
103635             return bounds
103636           };
103637           STRtreeNode.prototype.interfaces_ = function interfaces_ () {
103638             return []
103639           };
103640           STRtreeNode.prototype.getClass = function getClass () {
103641             return STRtreeNode
103642           };
103643
103644           return STRtreeNode;
103645         }(AbstractNode));
103646
103647         var SegmentPointComparator = function SegmentPointComparator () {};
103648
103649         SegmentPointComparator.prototype.interfaces_ = function interfaces_ () {
103650           return []
103651         };
103652         SegmentPointComparator.prototype.getClass = function getClass () {
103653           return SegmentPointComparator
103654         };
103655         SegmentPointComparator.relativeSign = function relativeSign (x0, x1) {
103656           if (x0 < x1) { return -1 }
103657           if (x0 > x1) { return 1 }
103658           return 0
103659         };
103660         SegmentPointComparator.compare = function compare (octant, p0, p1) {
103661           if (p0.equals2D(p1)) { return 0 }
103662           var xSign = SegmentPointComparator.relativeSign(p0.x, p1.x);
103663           var ySign = SegmentPointComparator.relativeSign(p0.y, p1.y);
103664           switch (octant) {
103665             case 0:
103666               return SegmentPointComparator.compareValue(xSign, ySign)
103667             case 1:
103668               return SegmentPointComparator.compareValue(ySign, xSign)
103669             case 2:
103670               return SegmentPointComparator.compareValue(ySign, -xSign)
103671             case 3:
103672               return SegmentPointComparator.compareValue(-xSign, ySign)
103673             case 4:
103674               return SegmentPointComparator.compareValue(-xSign, -ySign)
103675             case 5:
103676               return SegmentPointComparator.compareValue(-ySign, -xSign)
103677             case 6:
103678               return SegmentPointComparator.compareValue(-ySign, xSign)
103679             case 7:
103680               return SegmentPointComparator.compareValue(xSign, -ySign)
103681           }
103682           Assert.shouldNeverReachHere('invalid octant value');
103683           return 0
103684         };
103685         SegmentPointComparator.compareValue = function compareValue (compareSign0, compareSign1) {
103686           if (compareSign0 < 0) { return -1 }
103687           if (compareSign0 > 0) { return 1 }
103688           if (compareSign1 < 0) { return -1 }
103689           if (compareSign1 > 0) { return 1 }
103690           return 0
103691         };
103692
103693         var SegmentNode = function SegmentNode () {
103694           this._segString = null;
103695           this.coord = null;
103696           this.segmentIndex = null;
103697           this._segmentOctant = null;
103698           this._isInterior = null;
103699           var segString = arguments[0];
103700           var coord = arguments[1];
103701           var segmentIndex = arguments[2];
103702           var segmentOctant = arguments[3];
103703           this._segString = segString;
103704           this.coord = new Coordinate(coord);
103705           this.segmentIndex = segmentIndex;
103706           this._segmentOctant = segmentOctant;
103707           this._isInterior = !coord.equals2D(segString.getCoordinate(segmentIndex));
103708         };
103709         SegmentNode.prototype.getCoordinate = function getCoordinate () {
103710           return this.coord
103711         };
103712         SegmentNode.prototype.print = function print (out) {
103713           out.print(this.coord);
103714           out.print(' seg # = ' + this.segmentIndex);
103715         };
103716         SegmentNode.prototype.compareTo = function compareTo (obj) {
103717           var other = obj;
103718           if (this.segmentIndex < other.segmentIndex) { return -1 }
103719           if (this.segmentIndex > other.segmentIndex) { return 1 }
103720           if (this.coord.equals2D(other.coord)) { return 0 }
103721           return SegmentPointComparator.compare(this._segmentOctant, this.coord, other.coord)
103722         };
103723         SegmentNode.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
103724           if (this.segmentIndex === 0 && !this._isInterior) { return true }
103725           if (this.segmentIndex === maxSegmentIndex) { return true }
103726           return false
103727         };
103728         SegmentNode.prototype.isInterior = function isInterior () {
103729           return this._isInterior
103730         };
103731         SegmentNode.prototype.interfaces_ = function interfaces_ () {
103732           return [Comparable]
103733         };
103734         SegmentNode.prototype.getClass = function getClass () {
103735           return SegmentNode
103736         };
103737
103738         // import Iterator from '../../../../java/util/Iterator'
103739         var SegmentNodeList = function SegmentNodeList () {
103740           this._nodeMap = new TreeMap();
103741           this._edge = null;
103742           var edge = arguments[0];
103743           this._edge = edge;
103744         };
103745         SegmentNodeList.prototype.getSplitCoordinates = function getSplitCoordinates () {
103746             var this$1 = this;
103747
103748           var coordList = new CoordinateList();
103749           this.addEndpoints();
103750           var it = this.iterator();
103751           var eiPrev = it.next();
103752           while (it.hasNext()) {
103753             var ei = it.next();
103754             this$1.addEdgeCoordinates(eiPrev, ei, coordList);
103755             eiPrev = ei;
103756           }
103757           return coordList.toCoordinateArray()
103758         };
103759         SegmentNodeList.prototype.addCollapsedNodes = function addCollapsedNodes () {
103760             var this$1 = this;
103761
103762           var collapsedVertexIndexes = new ArrayList();
103763           this.findCollapsesFromInsertedNodes(collapsedVertexIndexes);
103764           this.findCollapsesFromExistingVertices(collapsedVertexIndexes);
103765           for (var it = collapsedVertexIndexes.iterator(); it.hasNext();) {
103766             var vertexIndex = it.next().intValue();
103767             this$1.add(this$1._edge.getCoordinate(vertexIndex), vertexIndex);
103768           }
103769         };
103770         SegmentNodeList.prototype.print = function print (out) {
103771           out.println('Intersections:');
103772           for (var it = this.iterator(); it.hasNext();) {
103773             var ei = it.next();
103774             ei.print(out);
103775           }
103776         };
103777         SegmentNodeList.prototype.findCollapsesFromExistingVertices = function findCollapsesFromExistingVertices (collapsedVertexIndexes) {
103778             var this$1 = this;
103779
103780           for (var i = 0; i < this._edge.size() - 2; i++) {
103781             var p0 = this$1._edge.getCoordinate(i);
103782             // const p1 = this._edge.getCoordinate(i + 1)
103783             var p2 = this$1._edge.getCoordinate(i + 2);
103784             if (p0.equals2D(p2)) {
103785               collapsedVertexIndexes.add(new Integer(i + 1));
103786             }
103787           }
103788         };
103789         SegmentNodeList.prototype.addEdgeCoordinates = function addEdgeCoordinates (ei0, ei1, coordList) {
103790             var this$1 = this;
103791
103792           // let npts = ei1.segmentIndex - ei0.segmentIndex + 2
103793           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103794           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103795           // if (!useIntPt1) {
103796           // npts--
103797           // }
103798           // const ipt = 0
103799           coordList.add(new Coordinate(ei0.coord), false);
103800           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103801             coordList.add(this$1._edge.getCoordinate(i));
103802           }
103803           if (useIntPt1) {
103804             coordList.add(new Coordinate(ei1.coord));
103805           }
103806         };
103807         SegmentNodeList.prototype.iterator = function iterator () {
103808           return this._nodeMap.values().iterator()
103809         };
103810         SegmentNodeList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
103811             var this$1 = this;
103812
103813           this.addEndpoints();
103814           this.addCollapsedNodes();
103815           var it = this.iterator();
103816           var eiPrev = it.next();
103817           while (it.hasNext()) {
103818             var ei = it.next();
103819             var newEdge = this$1.createSplitEdge(eiPrev, ei);
103820             edgeList.add(newEdge);
103821             eiPrev = ei;
103822           }
103823         };
103824         SegmentNodeList.prototype.findCollapseIndex = function findCollapseIndex (ei0, ei1, collapsedVertexIndex) {
103825           if (!ei0.coord.equals2D(ei1.coord)) { return false }
103826           var numVerticesBetween = ei1.segmentIndex - ei0.segmentIndex;
103827           if (!ei1.isInterior()) {
103828             numVerticesBetween--;
103829           }
103830           if (numVerticesBetween === 1) {
103831             collapsedVertexIndex[0] = ei0.segmentIndex + 1;
103832             return true
103833           }
103834           return false
103835         };
103836         SegmentNodeList.prototype.findCollapsesFromInsertedNodes = function findCollapsesFromInsertedNodes (collapsedVertexIndexes) {
103837             var this$1 = this;
103838
103839           var collapsedVertexIndex = new Array(1).fill(null);
103840           var it = this.iterator();
103841           var eiPrev = it.next();
103842           while (it.hasNext()) {
103843             var ei = it.next();
103844             var isCollapsed = this$1.findCollapseIndex(eiPrev, ei, collapsedVertexIndex);
103845             if (isCollapsed) { collapsedVertexIndexes.add(new Integer(collapsedVertexIndex[0])); }
103846             eiPrev = ei;
103847           }
103848         };
103849         SegmentNodeList.prototype.getEdge = function getEdge () {
103850           return this._edge
103851         };
103852         SegmentNodeList.prototype.addEndpoints = function addEndpoints () {
103853           var maxSegIndex = this._edge.size() - 1;
103854           this.add(this._edge.getCoordinate(0), 0);
103855           this.add(this._edge.getCoordinate(maxSegIndex), maxSegIndex);
103856         };
103857         SegmentNodeList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
103858             var this$1 = this;
103859
103860           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
103861           var lastSegStartPt = this._edge.getCoordinate(ei1.segmentIndex);
103862           var useIntPt1 = ei1.isInterior() || !ei1.coord.equals2D(lastSegStartPt);
103863           if (!useIntPt1) {
103864             npts--;
103865           }
103866           var pts = new Array(npts).fill(null);
103867           var ipt = 0;
103868           pts[ipt++] = new Coordinate(ei0.coord);
103869           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
103870             pts[ipt++] = this$1._edge.getCoordinate(i);
103871           }
103872           if (useIntPt1) { pts[ipt] = new Coordinate(ei1.coord); }
103873           return new NodedSegmentString(pts, this._edge.getData())
103874         };
103875         SegmentNodeList.prototype.add = function add (intPt, segmentIndex) {
103876           var eiNew = new SegmentNode(this._edge, intPt, segmentIndex, this._edge.getSegmentOctant(segmentIndex));
103877           var ei = this._nodeMap.get(eiNew);
103878           if (ei !== null) {
103879             Assert.isTrue(ei.coord.equals2D(intPt), 'Found equal nodes with different coordinates');
103880             return ei
103881           }
103882           this._nodeMap.put(eiNew, eiNew);
103883           return eiNew
103884         };
103885         SegmentNodeList.prototype.checkSplitEdgesCorrectness = function checkSplitEdgesCorrectness (splitEdges) {
103886           var edgePts = this._edge.getCoordinates();
103887           var split0 = splitEdges.get(0);
103888           var pt0 = split0.getCoordinate(0);
103889           if (!pt0.equals2D(edgePts[0])) { throw new RuntimeException('bad split edge start point at ' + pt0) }
103890           var splitn = splitEdges.get(splitEdges.size() - 1);
103891           var splitnPts = splitn.getCoordinates();
103892           var ptn = splitnPts[splitnPts.length - 1];
103893           if (!ptn.equals2D(edgePts[edgePts.length - 1])) { throw new RuntimeException('bad split edge end point at ' + ptn) }
103894         };
103895         SegmentNodeList.prototype.interfaces_ = function interfaces_ () {
103896           return []
103897         };
103898         SegmentNodeList.prototype.getClass = function getClass () {
103899           return SegmentNodeList
103900         };
103901
103902
103903
103904         // class NodeVertexIterator {
103905         //   constructor () {
103906         //     this._nodeList = null
103907         //     this._edge = null
103908         //     this._nodeIt = null
103909         //     this._currNode = null
103910         //     this._nextNode = null
103911         //     this._currSegIndex = 0
103912         //     let nodeList = arguments[0]
103913         //     this._nodeList = nodeList
103914         //     this._edge = nodeList.getEdge()
103915         //     this._nodeIt = nodeList.iterator()
103916         //     this.readNextNode()
103917         //   }
103918         //   next () {
103919         //     if (this._currNode === null) {
103920         //       this._currNode = this._nextNode
103921         //       this._currSegIndex = this._currNode.segmentIndex
103922         //       this.readNextNode()
103923         //       return this._currNode
103924         //     }
103925         //     if (this._nextNode === null) return null
103926         //     if (this._nextNode.segmentIndex === this._currNode.segmentIndex) {
103927         //       this._currNode = this._nextNode
103928         //       this._currSegIndex = this._currNode.segmentIndex
103929         //       this.readNextNode()
103930         //       return this._currNode
103931         //     }
103932         //     if (this._nextNode.segmentIndex > this._currNode.segmentIndex) {}
103933         //     return null
103934         //   }
103935         //   remove () {
103936         //     // throw new UnsupportedOperationException(this.getClass().getName())
103937         //   }
103938         //   hasNext () {
103939         //     if (this._nextNode === null) return false
103940         //     return true
103941         //   }
103942         //   readNextNode () {
103943         //     if (this._nodeIt.hasNext()) this._nextNode = this._nodeIt.next(); else this._nextNode = null
103944         //   }
103945         //   interfaces_ () {
103946         //     return [Iterator]
103947         //   }
103948         //   getClass () {
103949         //     return NodeVertexIterator
103950         //   }
103951         // }
103952
103953         var Octant = function Octant () {};
103954
103955         Octant.prototype.interfaces_ = function interfaces_ () {
103956           return []
103957         };
103958         Octant.prototype.getClass = function getClass () {
103959           return Octant
103960         };
103961         Octant.octant = function octant () {
103962           if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') {
103963             var dx = arguments[0];
103964             var dy = arguments[1];
103965             if (dx === 0.0 && dy === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for point ( ' + dx + ', ' + dy + ' )') }
103966             var adx = Math.abs(dx);
103967             var ady = Math.abs(dy);
103968             if (dx >= 0) {
103969               if (dy >= 0) {
103970                 if (adx >= ady) { return 0; } else { return 1 }
103971               } else {
103972                 if (adx >= ady) { return 7; } else { return 6 }
103973               }
103974             } else {
103975               if (dy >= 0) {
103976                 if (adx >= ady) { return 3; } else { return 2 }
103977               } else {
103978                 if (adx >= ady) { return 4; } else { return 5 }
103979               }
103980             }
103981           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) {
103982             var p0 = arguments[0];
103983             var p1 = arguments[1];
103984             var dx$1 = p1.x - p0.x;
103985             var dy$1 = p1.y - p0.y;
103986             if (dx$1 === 0.0 && dy$1 === 0.0) { throw new IllegalArgumentException('Cannot compute the octant for two identical points ' + p0) }
103987             return Octant.octant(dx$1, dy$1)
103988           }
103989         };
103990
103991         var SegmentString = function SegmentString () {};
103992
103993         SegmentString.prototype.getCoordinates = function getCoordinates () {};
103994         SegmentString.prototype.size = function size () {};
103995         SegmentString.prototype.getCoordinate = function getCoordinate (i) {};
103996         SegmentString.prototype.isClosed = function isClosed () {};
103997         SegmentString.prototype.setData = function setData (data) {};
103998         SegmentString.prototype.getData = function getData () {};
103999         SegmentString.prototype.interfaces_ = function interfaces_ () {
104000           return []
104001         };
104002         SegmentString.prototype.getClass = function getClass () {
104003           return SegmentString
104004         };
104005
104006         var NodableSegmentString = function NodableSegmentString () {};
104007
104008         NodableSegmentString.prototype.addIntersection = function addIntersection (intPt, segmentIndex) {};
104009         NodableSegmentString.prototype.interfaces_ = function interfaces_ () {
104010           return [SegmentString]
104011         };
104012         NodableSegmentString.prototype.getClass = function getClass () {
104013           return NodableSegmentString
104014         };
104015
104016         var NodedSegmentString = function NodedSegmentString () {
104017           this._nodeList = new SegmentNodeList(this);
104018           this._pts = null;
104019           this._data = null;
104020           var pts = arguments[0];
104021           var data = arguments[1];
104022           this._pts = pts;
104023           this._data = data;
104024         };
104025         NodedSegmentString.prototype.getCoordinates = function getCoordinates () {
104026           return this._pts
104027         };
104028         NodedSegmentString.prototype.size = function size () {
104029           return this._pts.length
104030         };
104031         NodedSegmentString.prototype.getCoordinate = function getCoordinate (i) {
104032           return this._pts[i]
104033         };
104034         NodedSegmentString.prototype.isClosed = function isClosed () {
104035           return this._pts[0].equals(this._pts[this._pts.length - 1])
104036         };
104037         NodedSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
104038           if (index === this._pts.length - 1) { return -1 }
104039           return this.safeOctant(this.getCoordinate(index), this.getCoordinate(index + 1))
104040         };
104041         NodedSegmentString.prototype.setData = function setData (data) {
104042           this._data = data;
104043         };
104044         NodedSegmentString.prototype.safeOctant = function safeOctant (p0, p1) {
104045           if (p0.equals2D(p1)) { return 0 }
104046           return Octant.octant(p0, p1)
104047         };
104048         NodedSegmentString.prototype.getData = function getData () {
104049           return this._data
104050         };
104051         NodedSegmentString.prototype.addIntersection = function addIntersection () {
104052           if (arguments.length === 2) {
104053             var intPt$1 = arguments[0];
104054             var segmentIndex = arguments[1];
104055             this.addIntersectionNode(intPt$1, segmentIndex);
104056           } else if (arguments.length === 4) {
104057             var li = arguments[0];
104058             var segmentIndex$1 = arguments[1];
104059             // const geomIndex = arguments[2]
104060             var intIndex = arguments[3];
104061             var intPt = new Coordinate(li.getIntersection(intIndex));
104062             this.addIntersection(intPt, segmentIndex$1);
104063           }
104064         };
104065         NodedSegmentString.prototype.toString = function toString () {
104066           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
104067         };
104068         NodedSegmentString.prototype.getNodeList = function getNodeList () {
104069           return this._nodeList
104070         };
104071         NodedSegmentString.prototype.addIntersectionNode = function addIntersectionNode (intPt, segmentIndex) {
104072           var normalizedSegmentIndex = segmentIndex;
104073           var nextSegIndex = normalizedSegmentIndex + 1;
104074           if (nextSegIndex < this._pts.length) {
104075             var nextPt = this._pts[nextSegIndex];
104076             if (intPt.equals2D(nextPt)) {
104077               normalizedSegmentIndex = nextSegIndex;
104078             }
104079           }
104080           var ei = this._nodeList.add(intPt, normalizedSegmentIndex);
104081           return ei
104082         };
104083         NodedSegmentString.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
104084             var this$1 = this;
104085
104086           for (var i = 0; i < li.getIntersectionNum(); i++) {
104087             this$1.addIntersection(li, segmentIndex, geomIndex, i);
104088           }
104089         };
104090         NodedSegmentString.prototype.interfaces_ = function interfaces_ () {
104091           return [NodableSegmentString]
104092         };
104093         NodedSegmentString.prototype.getClass = function getClass () {
104094           return NodedSegmentString
104095         };
104096         NodedSegmentString.getNodedSubstrings = function getNodedSubstrings () {
104097           if (arguments.length === 1) {
104098             var segStrings = arguments[0];
104099             var resultEdgelist = new ArrayList();
104100             NodedSegmentString.getNodedSubstrings(segStrings, resultEdgelist);
104101             return resultEdgelist
104102           } else if (arguments.length === 2) {
104103             var segStrings$1 = arguments[0];
104104             var resultEdgelist$1 = arguments[1];
104105             for (var i = segStrings$1.iterator(); i.hasNext();) {
104106               var ss = i.next();
104107               ss.getNodeList().addSplitEdges(resultEdgelist$1);
104108             }
104109           }
104110         };
104111
104112         var LineSegment = function LineSegment () {
104113           this.p0 = null;
104114           this.p1 = null;
104115           if (arguments.length === 0) {
104116             this.p0 = new Coordinate();
104117             this.p1 = new Coordinate();
104118           } else if (arguments.length === 1) {
104119             var ls = arguments[0];
104120             this.p0 = new Coordinate(ls.p0);
104121             this.p1 = new Coordinate(ls.p1);
104122           } else if (arguments.length === 2) {
104123             this.p0 = arguments[0];
104124             this.p1 = arguments[1];
104125           } else if (arguments.length === 4) {
104126             var x0 = arguments[0];
104127             var y0 = arguments[1];
104128             var x1 = arguments[2];
104129             var y1 = arguments[3];
104130             this.p0 = new Coordinate(x0, y0);
104131             this.p1 = new Coordinate(x1, y1);
104132           }
104133         };
104134
104135         var staticAccessors$24 = { serialVersionUID: { configurable: true } };
104136         LineSegment.prototype.minX = function minX () {
104137           return Math.min(this.p0.x, this.p1.x)
104138         };
104139         LineSegment.prototype.orientationIndex = function orientationIndex () {
104140           if (arguments[0] instanceof LineSegment) {
104141             var seg = arguments[0];
104142             var orient0 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p0);
104143             var orient1 = CGAlgorithms.orientationIndex(this.p0, this.p1, seg.p1);
104144             if (orient0 >= 0 && orient1 >= 0) { return Math.max(orient0, orient1) }
104145             if (orient0 <= 0 && orient1 <= 0) { return Math.max(orient0, orient1) }
104146             return 0
104147           } else if (arguments[0] instanceof Coordinate) {
104148             var p = arguments[0];
104149             return CGAlgorithms.orientationIndex(this.p0, this.p1, p)
104150           }
104151         };
104152         LineSegment.prototype.toGeometry = function toGeometry (geomFactory) {
104153           return geomFactory.createLineString([this.p0, this.p1])
104154         };
104155         LineSegment.prototype.isVertical = function isVertical () {
104156           return this.p0.x === this.p1.x
104157         };
104158         LineSegment.prototype.equals = function equals (o) {
104159           if (!(o instanceof LineSegment)) {
104160             return false
104161           }
104162           var other = o;
104163           return this.p0.equals(other.p0) && this.p1.equals(other.p1)
104164         };
104165         LineSegment.prototype.intersection = function intersection (line) {
104166           var li = new RobustLineIntersector();
104167           li.computeIntersection(this.p0, this.p1, line.p0, line.p1);
104168           if (li.hasIntersection()) { return li.getIntersection(0) }
104169           return null
104170         };
104171         LineSegment.prototype.project = function project () {
104172           if (arguments[0] instanceof Coordinate) {
104173             var p = arguments[0];
104174             if (p.equals(this.p0) || p.equals(this.p1)) { return new Coordinate(p) }
104175             var r = this.projectionFactor(p);
104176             var coord = new Coordinate();
104177             coord.x = this.p0.x + r * (this.p1.x - this.p0.x);
104178             coord.y = this.p0.y + r * (this.p1.y - this.p0.y);
104179             return coord
104180           } else if (arguments[0] instanceof LineSegment) {
104181             var seg = arguments[0];
104182             var pf0 = this.projectionFactor(seg.p0);
104183             var pf1 = this.projectionFactor(seg.p1);
104184             if (pf0 >= 1.0 && pf1 >= 1.0) { return null }
104185             if (pf0 <= 0.0 && pf1 <= 0.0) { return null }
104186             var newp0 = this.project(seg.p0);
104187             if (pf0 < 0.0) { newp0 = this.p0; }
104188             if (pf0 > 1.0) { newp0 = this.p1; }
104189             var newp1 = this.project(seg.p1);
104190             if (pf1 < 0.0) { newp1 = this.p0; }
104191             if (pf1 > 1.0) { newp1 = this.p1; }
104192             return new LineSegment(newp0, newp1)
104193           }
104194         };
104195         LineSegment.prototype.normalize = function normalize () {
104196           if (this.p1.compareTo(this.p0) < 0) { this.reverse(); }
104197         };
104198         LineSegment.prototype.angle = function angle () {
104199           return Math.atan2(this.p1.y - this.p0.y, this.p1.x - this.p0.x)
104200         };
104201         LineSegment.prototype.getCoordinate = function getCoordinate (i) {
104202           if (i === 0) { return this.p0 }
104203           return this.p1
104204         };
104205         LineSegment.prototype.distancePerpendicular = function distancePerpendicular (p) {
104206           return CGAlgorithms.distancePointLinePerpendicular(p, this.p0, this.p1)
104207         };
104208         LineSegment.prototype.minY = function minY () {
104209           return Math.min(this.p0.y, this.p1.y)
104210         };
104211         LineSegment.prototype.midPoint = function midPoint () {
104212           return LineSegment.midPoint(this.p0, this.p1)
104213         };
104214         LineSegment.prototype.projectionFactor = function projectionFactor (p) {
104215           if (p.equals(this.p0)) { return 0.0 }
104216           if (p.equals(this.p1)) { return 1.0 }
104217           var dx = this.p1.x - this.p0.x;
104218           var dy = this.p1.y - this.p0.y;
104219           var len = dx * dx + dy * dy;
104220           if (len <= 0.0) { return Double.NaN }
104221           var r = ((p.x - this.p0.x) * dx + (p.y - this.p0.y) * dy) / len;
104222           return r
104223         };
104224         LineSegment.prototype.closestPoints = function closestPoints (line) {
104225           var intPt = this.intersection(line);
104226           if (intPt !== null) {
104227             return [intPt, intPt]
104228           }
104229           var closestPt = new Array(2).fill(null);
104230           var minDistance = Double.MAX_VALUE;
104231           var dist = null;
104232           var close00 = this.closestPoint(line.p0);
104233           minDistance = close00.distance(line.p0);
104234           closestPt[0] = close00;
104235           closestPt[1] = line.p0;
104236           var close01 = this.closestPoint(line.p1);
104237           dist = close01.distance(line.p1);
104238           if (dist < minDistance) {
104239             minDistance = dist;
104240             closestPt[0] = close01;
104241             closestPt[1] = line.p1;
104242           }
104243           var close10 = line.closestPoint(this.p0);
104244           dist = close10.distance(this.p0);
104245           if (dist < minDistance) {
104246             minDistance = dist;
104247             closestPt[0] = this.p0;
104248             closestPt[1] = close10;
104249           }
104250           var close11 = line.closestPoint(this.p1);
104251           dist = close11.distance(this.p1);
104252           if (dist < minDistance) {
104253             minDistance = dist;
104254             closestPt[0] = this.p1;
104255             closestPt[1] = close11;
104256           }
104257           return closestPt
104258         };
104259         LineSegment.prototype.closestPoint = function closestPoint (p) {
104260           var factor = this.projectionFactor(p);
104261           if (factor > 0 && factor < 1) {
104262             return this.project(p)
104263           }
104264           var dist0 = this.p0.distance(p);
104265           var dist1 = this.p1.distance(p);
104266           if (dist0 < dist1) { return this.p0 }
104267           return this.p1
104268         };
104269         LineSegment.prototype.maxX = function maxX () {
104270           return Math.max(this.p0.x, this.p1.x)
104271         };
104272         LineSegment.prototype.getLength = function getLength () {
104273           return this.p0.distance(this.p1)
104274         };
104275         LineSegment.prototype.compareTo = function compareTo (o) {
104276           var other = o;
104277           var comp0 = this.p0.compareTo(other.p0);
104278           if (comp0 !== 0) { return comp0 }
104279           return this.p1.compareTo(other.p1)
104280         };
104281         LineSegment.prototype.reverse = function reverse () {
104282           var temp = this.p0;
104283           this.p0 = this.p1;
104284           this.p1 = temp;
104285         };
104286         LineSegment.prototype.equalsTopo = function equalsTopo (other) {
104287           return this.p0.equals(other.p0) &&
104288                 (this.p1.equals(other.p1) || this.p0.equals(other.p1)) &&
104289                  this.p1.equals(other.p0)
104290         };
104291         LineSegment.prototype.lineIntersection = function lineIntersection (line) {
104292           try {
104293             var intPt = HCoordinate.intersection(this.p0, this.p1, line.p0, line.p1);
104294             return intPt
104295           } catch (ex) {
104296             if (ex instanceof NotRepresentableException) ; else { throw ex }
104297           } finally {}
104298           return null
104299         };
104300         LineSegment.prototype.maxY = function maxY () {
104301           return Math.max(this.p0.y, this.p1.y)
104302         };
104303         LineSegment.prototype.pointAlongOffset = function pointAlongOffset (segmentLengthFraction, offsetDistance) {
104304           var segx = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104305           var segy = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104306           var dx = this.p1.x - this.p0.x;
104307           var dy = this.p1.y - this.p0.y;
104308           var len = Math.sqrt(dx * dx + dy * dy);
104309           var ux = 0.0;
104310           var uy = 0.0;
104311           if (offsetDistance !== 0.0) {
104312             if (len <= 0.0) { throw new Error('Cannot compute offset from zero-length line segment') }
104313             ux = offsetDistance * dx / len;
104314             uy = offsetDistance * dy / len;
104315           }
104316           var offsetx = segx - uy;
104317           var offsety = segy + ux;
104318           var coord = new Coordinate(offsetx, offsety);
104319           return coord
104320         };
104321         LineSegment.prototype.setCoordinates = function setCoordinates () {
104322           if (arguments.length === 1) {
104323             var ls = arguments[0];
104324             this.setCoordinates(ls.p0, ls.p1);
104325           } else if (arguments.length === 2) {
104326             var p0 = arguments[0];
104327             var p1 = arguments[1];
104328             this.p0.x = p0.x;
104329             this.p0.y = p0.y;
104330             this.p1.x = p1.x;
104331             this.p1.y = p1.y;
104332           }
104333         };
104334         LineSegment.prototype.segmentFraction = function segmentFraction (inputPt) {
104335           var segFrac = this.projectionFactor(inputPt);
104336           if (segFrac < 0.0) { segFrac = 0.0; } else if (segFrac > 1.0 || Double.isNaN(segFrac)) { segFrac = 1.0; }
104337           return segFrac
104338         };
104339         LineSegment.prototype.toString = function toString () {
104340           return 'LINESTRING( ' + this.p0.x + ' ' + this.p0.y + ', ' + this.p1.x + ' ' + this.p1.y + ')'
104341         };
104342         LineSegment.prototype.isHorizontal = function isHorizontal () {
104343           return this.p0.y === this.p1.y
104344         };
104345         LineSegment.prototype.distance = function distance () {
104346           if (arguments[0] instanceof LineSegment) {
104347             var ls = arguments[0];
104348             return CGAlgorithms.distanceLineLine(this.p0, this.p1, ls.p0, ls.p1)
104349           } else if (arguments[0] instanceof Coordinate) {
104350             var p = arguments[0];
104351             return CGAlgorithms.distancePointLine(p, this.p0, this.p1)
104352           }
104353         };
104354         LineSegment.prototype.pointAlong = function pointAlong (segmentLengthFraction) {
104355           var coord = new Coordinate();
104356           coord.x = this.p0.x + segmentLengthFraction * (this.p1.x - this.p0.x);
104357           coord.y = this.p0.y + segmentLengthFraction * (this.p1.y - this.p0.y);
104358           return coord
104359         };
104360         LineSegment.prototype.hashCode = function hashCode () {
104361           var bits0 = Double.doubleToLongBits(this.p0.x);
104362           bits0 ^= Double.doubleToLongBits(this.p0.y) * 31;
104363           var hash0 = Math.trunc(bits0) ^ Math.trunc(bits0 >> 32);
104364           var bits1 = Double.doubleToLongBits(this.p1.x);
104365           bits1 ^= Double.doubleToLongBits(this.p1.y) * 31;
104366           var hash1 = Math.trunc(bits1) ^ Math.trunc(bits1 >> 32);
104367           return hash0 ^ hash1
104368         };
104369         LineSegment.prototype.interfaces_ = function interfaces_ () {
104370           return [Comparable, Serializable]
104371         };
104372         LineSegment.prototype.getClass = function getClass () {
104373           return LineSegment
104374         };
104375         LineSegment.midPoint = function midPoint (p0, p1) {
104376           return new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2)
104377         };
104378         staticAccessors$24.serialVersionUID.get = function () { return 3252005833466256227 };
104379
104380         Object.defineProperties( LineSegment, staticAccessors$24 );
104381
104382         var MonotoneChainOverlapAction = function MonotoneChainOverlapAction () {
104383           this.tempEnv1 = new Envelope();
104384           this.tempEnv2 = new Envelope();
104385           this._overlapSeg1 = new LineSegment();
104386           this._overlapSeg2 = new LineSegment();
104387         };
104388         MonotoneChainOverlapAction.prototype.overlap = function overlap () {
104389           if (arguments.length === 2) ; else if (arguments.length === 4) {
104390             var mc1 = arguments[0];
104391             var start1 = arguments[1];
104392             var mc2 = arguments[2];
104393             var start2 = arguments[3];
104394             mc1.getLineSegment(start1, this._overlapSeg1);
104395             mc2.getLineSegment(start2, this._overlapSeg2);
104396             this.overlap(this._overlapSeg1, this._overlapSeg2);
104397           }
104398         };
104399         MonotoneChainOverlapAction.prototype.interfaces_ = function interfaces_ () {
104400           return []
104401         };
104402         MonotoneChainOverlapAction.prototype.getClass = function getClass () {
104403           return MonotoneChainOverlapAction
104404         };
104405
104406         var MonotoneChain = function MonotoneChain () {
104407           this._pts = null;
104408           this._start = null;
104409           this._end = null;
104410           this._env = null;
104411           this._context = null;
104412           this._id = null;
104413           var pts = arguments[0];
104414           var start = arguments[1];
104415           var end = arguments[2];
104416           var context = arguments[3];
104417           this._pts = pts;
104418           this._start = start;
104419           this._end = end;
104420           this._context = context;
104421         };
104422         MonotoneChain.prototype.getLineSegment = function getLineSegment (index, ls) {
104423           ls.p0 = this._pts[index];
104424           ls.p1 = this._pts[index + 1];
104425         };
104426         MonotoneChain.prototype.computeSelect = function computeSelect (searchEnv, start0, end0, mcs) {
104427           var p0 = this._pts[start0];
104428           var p1 = this._pts[end0];
104429           mcs.tempEnv1.init(p0, p1);
104430           if (end0 - start0 === 1) {
104431             mcs.select(this, start0);
104432             return null
104433           }
104434           if (!searchEnv.intersects(mcs.tempEnv1)) { return null }
104435           var mid = Math.trunc((start0 + end0) / 2);
104436           if (start0 < mid) {
104437             this.computeSelect(searchEnv, start0, mid, mcs);
104438           }
104439           if (mid < end0) {
104440             this.computeSelect(searchEnv, mid, end0, mcs);
104441           }
104442         };
104443         MonotoneChain.prototype.getCoordinates = function getCoordinates () {
104444             var this$1 = this;
104445
104446           var coord = new Array(this._end - this._start + 1).fill(null);
104447           var index = 0;
104448           for (var i = this._start; i <= this._end; i++) {
104449             coord[index++] = this$1._pts[i];
104450           }
104451           return coord
104452         };
104453         MonotoneChain.prototype.computeOverlaps = function computeOverlaps (mc, mco) {
104454           this.computeOverlapsInternal(this._start, this._end, mc, mc._start, mc._end, mco);
104455         };
104456         MonotoneChain.prototype.setId = function setId (id) {
104457           this._id = id;
104458         };
104459         MonotoneChain.prototype.select = function select (searchEnv, mcs) {
104460           this.computeSelect(searchEnv, this._start, this._end, mcs);
104461         };
104462         MonotoneChain.prototype.getEnvelope = function getEnvelope () {
104463           if (this._env === null) {
104464             var p0 = this._pts[this._start];
104465             var p1 = this._pts[this._end];
104466             this._env = new Envelope(p0, p1);
104467           }
104468           return this._env
104469         };
104470         MonotoneChain.prototype.getEndIndex = function getEndIndex () {
104471           return this._end
104472         };
104473         MonotoneChain.prototype.getStartIndex = function getStartIndex () {
104474           return this._start
104475         };
104476         MonotoneChain.prototype.getContext = function getContext () {
104477           return this._context
104478         };
104479         MonotoneChain.prototype.getId = function getId () {
104480           return this._id
104481         };
104482         MonotoneChain.prototype.computeOverlapsInternal = function computeOverlapsInternal (start0, end0, mc, start1, end1, mco) {
104483           var p00 = this._pts[start0];
104484           var p01 = this._pts[end0];
104485           var p10 = mc._pts[start1];
104486           var p11 = mc._pts[end1];
104487           if (end0 - start0 === 1 && end1 - start1 === 1) {
104488             mco.overlap(this, start0, mc, start1);
104489             return null
104490           }
104491           mco.tempEnv1.init(p00, p01);
104492           mco.tempEnv2.init(p10, p11);
104493           if (!mco.tempEnv1.intersects(mco.tempEnv2)) { return null }
104494           var mid0 = Math.trunc((start0 + end0) / 2);
104495           var mid1 = Math.trunc((start1 + end1) / 2);
104496           if (start0 < mid0) {
104497             if (start1 < mid1) { this.computeOverlapsInternal(start0, mid0, mc, start1, mid1, mco); }
104498             if (mid1 < end1) { this.computeOverlapsInternal(start0, mid0, mc, mid1, end1, mco); }
104499           }
104500           if (mid0 < end0) {
104501             if (start1 < mid1) { this.computeOverlapsInternal(mid0, end0, mc, start1, mid1, mco); }
104502             if (mid1 < end1) { this.computeOverlapsInternal(mid0, end0, mc, mid1, end1, mco); }
104503           }
104504         };
104505         MonotoneChain.prototype.interfaces_ = function interfaces_ () {
104506           return []
104507         };
104508         MonotoneChain.prototype.getClass = function getClass () {
104509           return MonotoneChain
104510         };
104511
104512         var MonotoneChainBuilder = function MonotoneChainBuilder () {};
104513
104514         MonotoneChainBuilder.prototype.interfaces_ = function interfaces_ () {
104515           return []
104516         };
104517         MonotoneChainBuilder.prototype.getClass = function getClass () {
104518           return MonotoneChainBuilder
104519         };
104520         MonotoneChainBuilder.getChainStartIndices = function getChainStartIndices (pts) {
104521           var start = 0;
104522           var startIndexList = new ArrayList();
104523           startIndexList.add(new Integer(start));
104524           do {
104525             var last = MonotoneChainBuilder.findChainEnd(pts, start);
104526             startIndexList.add(new Integer(last));
104527             start = last;
104528           } while (start < pts.length - 1)
104529           var startIndex = MonotoneChainBuilder.toIntArray(startIndexList);
104530           return startIndex
104531         };
104532         MonotoneChainBuilder.findChainEnd = function findChainEnd (pts, start) {
104533           var safeStart = start;
104534           while (safeStart < pts.length - 1 && pts[safeStart].equals2D(pts[safeStart + 1])) {
104535             safeStart++;
104536           }
104537           if (safeStart >= pts.length - 1) {
104538             return pts.length - 1
104539           }
104540           var chainQuad = Quadrant.quadrant(pts[safeStart], pts[safeStart + 1]);
104541           var last = start + 1;
104542           while (last < pts.length) {
104543             if (!pts[last - 1].equals2D(pts[last])) {
104544               var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
104545               if (quad !== chainQuad) { break }
104546             }
104547             last++;
104548           }
104549           return last - 1
104550         };
104551         MonotoneChainBuilder.getChains = function getChains () {
104552           if (arguments.length === 1) {
104553             var pts = arguments[0];
104554             return MonotoneChainBuilder.getChains(pts, null)
104555           } else if (arguments.length === 2) {
104556             var pts$1 = arguments[0];
104557             var context = arguments[1];
104558             var mcList = new ArrayList();
104559             var startIndex = MonotoneChainBuilder.getChainStartIndices(pts$1);
104560             for (var i = 0; i < startIndex.length - 1; i++) {
104561               var mc = new MonotoneChain(pts$1, startIndex[i], startIndex[i + 1], context);
104562               mcList.add(mc);
104563             }
104564             return mcList
104565           }
104566         };
104567         MonotoneChainBuilder.toIntArray = function toIntArray (list) {
104568           var array = new Array(list.size()).fill(null);
104569           for (var i = 0; i < array.length; i++) {
104570             array[i] = list.get(i).intValue();
104571           }
104572           return array
104573         };
104574
104575         var Noder = function Noder () {};
104576
104577         Noder.prototype.computeNodes = function computeNodes (segStrings) {};
104578         Noder.prototype.getNodedSubstrings = function getNodedSubstrings () {};
104579         Noder.prototype.interfaces_ = function interfaces_ () {
104580           return []
104581         };
104582         Noder.prototype.getClass = function getClass () {
104583           return Noder
104584         };
104585
104586         var SinglePassNoder = function SinglePassNoder () {
104587           this._segInt = null;
104588           if (arguments.length === 0) ; else if (arguments.length === 1) {
104589             var segInt = arguments[0];
104590             this.setSegmentIntersector(segInt);
104591           }
104592         };
104593         SinglePassNoder.prototype.setSegmentIntersector = function setSegmentIntersector (segInt) {
104594           this._segInt = segInt;
104595         };
104596         SinglePassNoder.prototype.interfaces_ = function interfaces_ () {
104597           return [Noder]
104598         };
104599         SinglePassNoder.prototype.getClass = function getClass () {
104600           return SinglePassNoder
104601         };
104602
104603         var MCIndexNoder = (function (SinglePassNoder$$1) {
104604           function MCIndexNoder (si) {
104605             if (si) { SinglePassNoder$$1.call(this, si); }
104606             else { SinglePassNoder$$1.call(this); }
104607             this._monoChains = new ArrayList();
104608             this._index = new STRtree();
104609             this._idCounter = 0;
104610             this._nodedSegStrings = null;
104611             this._nOverlaps = 0;
104612           }
104613
104614           if ( SinglePassNoder$$1 ) { MCIndexNoder.__proto__ = SinglePassNoder$$1; }
104615           MCIndexNoder.prototype = Object.create( SinglePassNoder$$1 && SinglePassNoder$$1.prototype );
104616           MCIndexNoder.prototype.constructor = MCIndexNoder;
104617
104618           var staticAccessors = { SegmentOverlapAction: { configurable: true } };
104619           MCIndexNoder.prototype.getMonotoneChains = function getMonotoneChains () {
104620             return this._monoChains
104621           };
104622           MCIndexNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
104623             return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
104624           };
104625           MCIndexNoder.prototype.getIndex = function getIndex () {
104626             return this._index
104627           };
104628           MCIndexNoder.prototype.add = function add (segStr) {
104629             var this$1 = this;
104630
104631             var segChains = MonotoneChainBuilder.getChains(segStr.getCoordinates(), segStr);
104632             for (var i = segChains.iterator(); i.hasNext();) {
104633               var mc = i.next();
104634               mc.setId(this$1._idCounter++);
104635               this$1._index.insert(mc.getEnvelope(), mc);
104636               this$1._monoChains.add(mc);
104637             }
104638           };
104639           MCIndexNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
104640             var this$1 = this;
104641
104642             this._nodedSegStrings = inputSegStrings;
104643             for (var i = inputSegStrings.iterator(); i.hasNext();) {
104644               this$1.add(i.next());
104645             }
104646             this.intersectChains();
104647           };
104648           MCIndexNoder.prototype.intersectChains = function intersectChains () {
104649             var this$1 = this;
104650
104651             var overlapAction = new SegmentOverlapAction(this._segInt);
104652             for (var i = this._monoChains.iterator(); i.hasNext();) {
104653               var queryChain = i.next();
104654               var overlapChains = this$1._index.query(queryChain.getEnvelope());
104655               for (var j = overlapChains.iterator(); j.hasNext();) {
104656                 var testChain = j.next();
104657                 if (testChain.getId() > queryChain.getId()) {
104658                   queryChain.computeOverlaps(testChain, overlapAction);
104659                   this$1._nOverlaps++;
104660                 }
104661                 if (this$1._segInt.isDone()) { return null }
104662               }
104663             }
104664           };
104665           MCIndexNoder.prototype.interfaces_ = function interfaces_ () {
104666             return []
104667           };
104668           MCIndexNoder.prototype.getClass = function getClass () {
104669             return MCIndexNoder
104670           };
104671           staticAccessors.SegmentOverlapAction.get = function () { return SegmentOverlapAction };
104672
104673           Object.defineProperties( MCIndexNoder, staticAccessors );
104674
104675           return MCIndexNoder;
104676         }(SinglePassNoder));
104677
104678         var SegmentOverlapAction = (function (MonotoneChainOverlapAction$$1) {
104679           function SegmentOverlapAction () {
104680             MonotoneChainOverlapAction$$1.call(this);
104681             this._si = null;
104682             var si = arguments[0];
104683             this._si = si;
104684           }
104685
104686           if ( MonotoneChainOverlapAction$$1 ) { SegmentOverlapAction.__proto__ = MonotoneChainOverlapAction$$1; }
104687           SegmentOverlapAction.prototype = Object.create( MonotoneChainOverlapAction$$1 && MonotoneChainOverlapAction$$1.prototype );
104688           SegmentOverlapAction.prototype.constructor = SegmentOverlapAction;
104689           SegmentOverlapAction.prototype.overlap = function overlap () {
104690             if (arguments.length === 4) {
104691               var mc1 = arguments[0];
104692               var start1 = arguments[1];
104693               var mc2 = arguments[2];
104694               var start2 = arguments[3];
104695               var ss1 = mc1.getContext();
104696               var ss2 = mc2.getContext();
104697               this._si.processIntersections(ss1, start1, ss2, start2);
104698             } else { return MonotoneChainOverlapAction$$1.prototype.overlap.apply(this, arguments) }
104699           };
104700           SegmentOverlapAction.prototype.interfaces_ = function interfaces_ () {
104701             return []
104702           };
104703           SegmentOverlapAction.prototype.getClass = function getClass () {
104704             return SegmentOverlapAction
104705           };
104706
104707           return SegmentOverlapAction;
104708         }(MonotoneChainOverlapAction));
104709
104710         var BufferParameters = function BufferParameters () {
104711           this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104712           this._endCapStyle = BufferParameters.CAP_ROUND;
104713           this._joinStyle = BufferParameters.JOIN_ROUND;
104714           this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT;
104715           this._isSingleSided = false;
104716           this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR;
104717
104718           if (arguments.length === 0) ; else if (arguments.length === 1) {
104719             var quadrantSegments = arguments[0];
104720             this.setQuadrantSegments(quadrantSegments);
104721           } else if (arguments.length === 2) {
104722             var quadrantSegments$1 = arguments[0];
104723             var endCapStyle = arguments[1];
104724             this.setQuadrantSegments(quadrantSegments$1);
104725             this.setEndCapStyle(endCapStyle);
104726           } else if (arguments.length === 4) {
104727             var quadrantSegments$2 = arguments[0];
104728             var endCapStyle$1 = arguments[1];
104729             var joinStyle = arguments[2];
104730             var mitreLimit = arguments[3];
104731             this.setQuadrantSegments(quadrantSegments$2);
104732             this.setEndCapStyle(endCapStyle$1);
104733             this.setJoinStyle(joinStyle);
104734             this.setMitreLimit(mitreLimit);
104735           }
104736         };
104737
104738         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 } };
104739         BufferParameters.prototype.getEndCapStyle = function getEndCapStyle () {
104740           return this._endCapStyle
104741         };
104742         BufferParameters.prototype.isSingleSided = function isSingleSided () {
104743           return this._isSingleSided
104744         };
104745         BufferParameters.prototype.setQuadrantSegments = function setQuadrantSegments (quadSegs) {
104746           this._quadrantSegments = quadSegs;
104747           if (this._quadrantSegments === 0) { this._joinStyle = BufferParameters.JOIN_BEVEL; }
104748           if (this._quadrantSegments < 0) {
104749             this._joinStyle = BufferParameters.JOIN_MITRE;
104750             this._mitreLimit = Math.abs(this._quadrantSegments);
104751           }
104752           if (quadSegs <= 0) {
104753             this._quadrantSegments = 1;
104754           }
104755           if (this._joinStyle !== BufferParameters.JOIN_ROUND) {
104756             this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS;
104757           }
104758         };
104759         BufferParameters.prototype.getJoinStyle = function getJoinStyle () {
104760           return this._joinStyle
104761         };
104762         BufferParameters.prototype.setJoinStyle = function setJoinStyle (joinStyle) {
104763           this._joinStyle = joinStyle;
104764         };
104765         BufferParameters.prototype.setSimplifyFactor = function setSimplifyFactor (simplifyFactor) {
104766           this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor;
104767         };
104768         BufferParameters.prototype.getSimplifyFactor = function getSimplifyFactor () {
104769           return this._simplifyFactor
104770         };
104771         BufferParameters.prototype.getQuadrantSegments = function getQuadrantSegments () {
104772           return this._quadrantSegments
104773         };
104774         BufferParameters.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
104775           this._endCapStyle = endCapStyle;
104776         };
104777         BufferParameters.prototype.getMitreLimit = function getMitreLimit () {
104778           return this._mitreLimit
104779         };
104780         BufferParameters.prototype.setMitreLimit = function setMitreLimit (mitreLimit) {
104781           this._mitreLimit = mitreLimit;
104782         };
104783         BufferParameters.prototype.setSingleSided = function setSingleSided (isSingleSided) {
104784           this._isSingleSided = isSingleSided;
104785         };
104786         BufferParameters.prototype.interfaces_ = function interfaces_ () {
104787           return []
104788         };
104789         BufferParameters.prototype.getClass = function getClass () {
104790           return BufferParameters
104791         };
104792         BufferParameters.bufferDistanceError = function bufferDistanceError (quadSegs) {
104793           var alpha = Math.PI / 2.0 / quadSegs;
104794           return 1 - Math.cos(alpha / 2.0)
104795         };
104796         staticAccessors$25.CAP_ROUND.get = function () { return 1 };
104797         staticAccessors$25.CAP_FLAT.get = function () { return 2 };
104798         staticAccessors$25.CAP_SQUARE.get = function () { return 3 };
104799         staticAccessors$25.JOIN_ROUND.get = function () { return 1 };
104800         staticAccessors$25.JOIN_MITRE.get = function () { return 2 };
104801         staticAccessors$25.JOIN_BEVEL.get = function () { return 3 };
104802         staticAccessors$25.DEFAULT_QUADRANT_SEGMENTS.get = function () { return 8 };
104803         staticAccessors$25.DEFAULT_MITRE_LIMIT.get = function () { return 5.0 };
104804         staticAccessors$25.DEFAULT_SIMPLIFY_FACTOR.get = function () { return 0.01 };
104805
104806         Object.defineProperties( BufferParameters, staticAccessors$25 );
104807
104808         var BufferInputLineSimplifier = function BufferInputLineSimplifier (inputLine) {
104809           this._distanceTol = null;
104810           this._isDeleted = null;
104811           this._angleOrientation = CGAlgorithms.COUNTERCLOCKWISE;
104812           this._inputLine = inputLine || null;
104813         };
104814
104815         var staticAccessors$26 = { INIT: { configurable: true },DELETE: { configurable: true },KEEP: { configurable: true },NUM_PTS_TO_CHECK: { configurable: true } };
104816         BufferInputLineSimplifier.prototype.isDeletable = function isDeletable (i0, i1, i2, distanceTol) {
104817           var p0 = this._inputLine[i0];
104818           var p1 = this._inputLine[i1];
104819           var p2 = this._inputLine[i2];
104820           if (!this.isConcave(p0, p1, p2)) { return false }
104821           if (!this.isShallow(p0, p1, p2, distanceTol)) { return false }
104822           return this.isShallowSampled(p0, p1, i0, i2, distanceTol)
104823         };
104824         BufferInputLineSimplifier.prototype.deleteShallowConcavities = function deleteShallowConcavities () {
104825             var this$1 = this;
104826
104827           var index = 1;
104828           // const maxIndex = this._inputLine.length - 1
104829           var midIndex = this.findNextNonDeletedIndex(index);
104830           var lastIndex = this.findNextNonDeletedIndex(midIndex);
104831           var isChanged = false;
104832           while (lastIndex < this._inputLine.length) {
104833             var isMiddleVertexDeleted = false;
104834             if (this$1.isDeletable(index, midIndex, lastIndex, this$1._distanceTol)) {
104835               this$1._isDeleted[midIndex] = BufferInputLineSimplifier.DELETE;
104836               isMiddleVertexDeleted = true;
104837               isChanged = true;
104838             }
104839             if (isMiddleVertexDeleted) { index = lastIndex; } else { index = midIndex; }
104840             midIndex = this$1.findNextNonDeletedIndex(index);
104841             lastIndex = this$1.findNextNonDeletedIndex(midIndex);
104842           }
104843           return isChanged
104844         };
104845         BufferInputLineSimplifier.prototype.isShallowConcavity = function isShallowConcavity (p0, p1, p2, distanceTol) {
104846           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104847           var isAngleToSimplify = orientation === this._angleOrientation;
104848           if (!isAngleToSimplify) { return false }
104849           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104850           return dist < distanceTol
104851         };
104852         BufferInputLineSimplifier.prototype.isShallowSampled = function isShallowSampled (p0, p2, i0, i2, distanceTol) {
104853             var this$1 = this;
104854
104855           var inc = Math.trunc((i2 - i0) / BufferInputLineSimplifier.NUM_PTS_TO_CHECK);
104856           if (inc <= 0) { inc = 1; }
104857           for (var i = i0; i < i2; i += inc) {
104858             if (!this$1.isShallow(p0, p2, this$1._inputLine[i], distanceTol)) { return false }
104859           }
104860           return true
104861         };
104862         BufferInputLineSimplifier.prototype.isConcave = function isConcave (p0, p1, p2) {
104863           var orientation = CGAlgorithms.computeOrientation(p0, p1, p2);
104864           var isConcave = orientation === this._angleOrientation;
104865           return isConcave
104866         };
104867         BufferInputLineSimplifier.prototype.simplify = function simplify (distanceTol) {
104868             var this$1 = this;
104869
104870           this._distanceTol = Math.abs(distanceTol);
104871           if (distanceTol < 0) { this._angleOrientation = CGAlgorithms.CLOCKWISE; }
104872           this._isDeleted = new Array(this._inputLine.length).fill(null);
104873           var isChanged = false;
104874           do {
104875             isChanged = this$1.deleteShallowConcavities();
104876           } while (isChanged)
104877           return this.collapseLine()
104878         };
104879         BufferInputLineSimplifier.prototype.findNextNonDeletedIndex = function findNextNonDeletedIndex (index) {
104880           var next = index + 1;
104881           while (next < this._inputLine.length && this._isDeleted[next] === BufferInputLineSimplifier.DELETE) { next++; }
104882           return next
104883         };
104884         BufferInputLineSimplifier.prototype.isShallow = function isShallow (p0, p1, p2, distanceTol) {
104885           var dist = CGAlgorithms.distancePointLine(p1, p0, p2);
104886           return dist < distanceTol
104887         };
104888         BufferInputLineSimplifier.prototype.collapseLine = function collapseLine () {
104889             var this$1 = this;
104890
104891           var coordList = new CoordinateList();
104892           for (var i = 0; i < this._inputLine.length; i++) {
104893             if (this$1._isDeleted[i] !== BufferInputLineSimplifier.DELETE) { coordList.add(this$1._inputLine[i]); }
104894           }
104895           return coordList.toCoordinateArray()
104896         };
104897         BufferInputLineSimplifier.prototype.interfaces_ = function interfaces_ () {
104898           return []
104899         };
104900         BufferInputLineSimplifier.prototype.getClass = function getClass () {
104901           return BufferInputLineSimplifier
104902         };
104903         BufferInputLineSimplifier.simplify = function simplify (inputLine, distanceTol) {
104904           var simp = new BufferInputLineSimplifier(inputLine);
104905           return simp.simplify(distanceTol)
104906         };
104907         staticAccessors$26.INIT.get = function () { return 0 };
104908         staticAccessors$26.DELETE.get = function () { return 1 };
104909         staticAccessors$26.KEEP.get = function () { return 1 };
104910         staticAccessors$26.NUM_PTS_TO_CHECK.get = function () { return 10 };
104911
104912         Object.defineProperties( BufferInputLineSimplifier, staticAccessors$26 );
104913
104914         var OffsetSegmentString = function OffsetSegmentString () {
104915           this._ptList = null;
104916           this._precisionModel = null;
104917           this._minimimVertexDistance = 0.0;
104918           this._ptList = new ArrayList();
104919         };
104920
104921         var staticAccessors$28 = { COORDINATE_ARRAY_TYPE: { configurable: true } };
104922         OffsetSegmentString.prototype.getCoordinates = function getCoordinates () {
104923           var coord = this._ptList.toArray(OffsetSegmentString.COORDINATE_ARRAY_TYPE);
104924           return coord
104925         };
104926         OffsetSegmentString.prototype.setPrecisionModel = function setPrecisionModel (precisionModel) {
104927           this._precisionModel = precisionModel;
104928         };
104929         OffsetSegmentString.prototype.addPt = function addPt (pt) {
104930           var bufPt = new Coordinate(pt);
104931           this._precisionModel.makePrecise(bufPt);
104932           if (this.isRedundant(bufPt)) { return null }
104933           this._ptList.add(bufPt);
104934         };
104935         OffsetSegmentString.prototype.revere = function revere () {};
104936         OffsetSegmentString.prototype.addPts = function addPts (pt, isForward) {
104937             var this$1 = this;
104938
104939           if (isForward) {
104940             for (var i = 0; i < pt.length; i++) {
104941               this$1.addPt(pt[i]);
104942             }
104943           } else {
104944             for (var i$1 = pt.length - 1; i$1 >= 0; i$1--) {
104945               this$1.addPt(pt[i$1]);
104946             }
104947           }
104948         };
104949         OffsetSegmentString.prototype.isRedundant = function isRedundant (pt) {
104950           if (this._ptList.size() < 1) { return false }
104951           var lastPt = this._ptList.get(this._ptList.size() - 1);
104952           var ptDist = pt.distance(lastPt);
104953           if (ptDist < this._minimimVertexDistance) { return true }
104954           return false
104955         };
104956         OffsetSegmentString.prototype.toString = function toString () {
104957           var fact = new GeometryFactory();
104958           var line = fact.createLineString(this.getCoordinates());
104959           return line.toString()
104960         };
104961         OffsetSegmentString.prototype.closeRing = function closeRing () {
104962           if (this._ptList.size() < 1) { return null }
104963           var startPt = new Coordinate(this._ptList.get(0));
104964           var lastPt = this._ptList.get(this._ptList.size() - 1);
104965           // const last2Pt = null
104966           // if (this._ptList.size() >= 2) last2Pt = this._ptList.get(this._ptList.size() - 2)
104967           if (startPt.equals(lastPt)) { return null }
104968           this._ptList.add(startPt);
104969         };
104970         OffsetSegmentString.prototype.setMinimumVertexDistance = function setMinimumVertexDistance (minimimVertexDistance) {
104971           this._minimimVertexDistance = minimimVertexDistance;
104972         };
104973         OffsetSegmentString.prototype.interfaces_ = function interfaces_ () {
104974           return []
104975         };
104976         OffsetSegmentString.prototype.getClass = function getClass () {
104977           return OffsetSegmentString
104978         };
104979         staticAccessors$28.COORDINATE_ARRAY_TYPE.get = function () { return new Array(0).fill(null) };
104980
104981         Object.defineProperties( OffsetSegmentString, staticAccessors$28 );
104982
104983         var Angle = function Angle () {};
104984
104985         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 } };
104986
104987         Angle.prototype.interfaces_ = function interfaces_ () {
104988           return []
104989         };
104990         Angle.prototype.getClass = function getClass () {
104991           return Angle
104992         };
104993         Angle.toDegrees = function toDegrees (radians) {
104994           return radians * 180 / Math.PI
104995         };
104996         Angle.normalize = function normalize (angle) {
104997           while (angle > Math.PI) { angle -= Angle.PI_TIMES_2; }
104998           while (angle <= -Math.PI) { angle += Angle.PI_TIMES_2; }
104999           return angle
105000         };
105001         Angle.angle = function angle () {
105002           if (arguments.length === 1) {
105003             var p = arguments[0];
105004             return Math.atan2(p.y, p.x)
105005           } else if (arguments.length === 2) {
105006             var p0 = arguments[0];
105007             var p1 = arguments[1];
105008             var dx = p1.x - p0.x;
105009             var dy = p1.y - p0.y;
105010             return Math.atan2(dy, dx)
105011           }
105012         };
105013         Angle.isAcute = function isAcute (p0, p1, p2) {
105014           var dx0 = p0.x - p1.x;
105015           var dy0 = p0.y - p1.y;
105016           var dx1 = p2.x - p1.x;
105017           var dy1 = p2.y - p1.y;
105018           var dotprod = dx0 * dx1 + dy0 * dy1;
105019           return dotprod > 0
105020         };
105021         Angle.isObtuse = function isObtuse (p0, p1, p2) {
105022           var dx0 = p0.x - p1.x;
105023           var dy0 = p0.y - p1.y;
105024           var dx1 = p2.x - p1.x;
105025           var dy1 = p2.y - p1.y;
105026           var dotprod = dx0 * dx1 + dy0 * dy1;
105027           return dotprod < 0
105028         };
105029         Angle.interiorAngle = function interiorAngle (p0, p1, p2) {
105030           var anglePrev = Angle.angle(p1, p0);
105031           var angleNext = Angle.angle(p1, p2);
105032           return Math.abs(angleNext - anglePrev)
105033         };
105034         Angle.normalizePositive = function normalizePositive (angle) {
105035           if (angle < 0.0) {
105036             while (angle < 0.0) { angle += Angle.PI_TIMES_2; }
105037             if (angle >= Angle.PI_TIMES_2) { angle = 0.0; }
105038           } else {
105039             while (angle >= Angle.PI_TIMES_2) { angle -= Angle.PI_TIMES_2; }
105040             if (angle < 0.0) { angle = 0.0; }
105041           }
105042           return angle
105043         };
105044         Angle.angleBetween = function angleBetween (tip1, tail, tip2) {
105045           var a1 = Angle.angle(tail, tip1);
105046           var a2 = Angle.angle(tail, tip2);
105047           return Angle.diff(a1, a2)
105048         };
105049         Angle.diff = function diff (ang1, ang2) {
105050           var delAngle = null;
105051           if (ang1 < ang2) {
105052             delAngle = ang2 - ang1;
105053           } else {
105054             delAngle = ang1 - ang2;
105055           }
105056           if (delAngle > Math.PI) {
105057             delAngle = 2 * Math.PI - delAngle;
105058           }
105059           return delAngle
105060         };
105061         Angle.toRadians = function toRadians (angleDegrees) {
105062           return angleDegrees * Math.PI / 180.0
105063         };
105064         Angle.getTurn = function getTurn (ang1, ang2) {
105065           var crossproduct = Math.sin(ang2 - ang1);
105066           if (crossproduct > 0) {
105067             return Angle.COUNTERCLOCKWISE
105068           }
105069           if (crossproduct < 0) {
105070             return Angle.CLOCKWISE
105071           }
105072           return Angle.NONE
105073         };
105074         Angle.angleBetweenOriented = function angleBetweenOriented (tip1, tail, tip2) {
105075           var a1 = Angle.angle(tail, tip1);
105076           var a2 = Angle.angle(tail, tip2);
105077           var angDel = a2 - a1;
105078           if (angDel <= -Math.PI) { return angDel + Angle.PI_TIMES_2 }
105079           if (angDel > Math.PI) { return angDel - Angle.PI_TIMES_2 }
105080           return angDel
105081         };
105082         staticAccessors$29.PI_TIMES_2.get = function () { return 2.0 * Math.PI };
105083         staticAccessors$29.PI_OVER_2.get = function () { return Math.PI / 2.0 };
105084         staticAccessors$29.PI_OVER_4.get = function () { return Math.PI / 4.0 };
105085         staticAccessors$29.COUNTERCLOCKWISE.get = function () { return CGAlgorithms.COUNTERCLOCKWISE };
105086         staticAccessors$29.CLOCKWISE.get = function () { return CGAlgorithms.CLOCKWISE };
105087         staticAccessors$29.NONE.get = function () { return CGAlgorithms.COLLINEAR };
105088
105089         Object.defineProperties( Angle, staticAccessors$29 );
105090
105091         var OffsetSegmentGenerator = function OffsetSegmentGenerator () {
105092           this._maxCurveSegmentError = 0.0;
105093           this._filletAngleQuantum = null;
105094           this._closingSegLengthFactor = 1;
105095           this._segList = null;
105096           this._distance = 0.0;
105097           this._precisionModel = null;
105098           this._bufParams = null;
105099           this._li = null;
105100           this._s0 = null;
105101           this._s1 = null;
105102           this._s2 = null;
105103           this._seg0 = new LineSegment();
105104           this._seg1 = new LineSegment();
105105           this._offset0 = new LineSegment();
105106           this._offset1 = new LineSegment();
105107           this._side = 0;
105108           this._hasNarrowConcaveAngle = false;
105109           var precisionModel = arguments[0];
105110           var bufParams = arguments[1];
105111           var distance = arguments[2];
105112           this._precisionModel = precisionModel;
105113           this._bufParams = bufParams;
105114           this._li = new RobustLineIntersector();
105115           this._filletAngleQuantum = Math.PI / 2.0 / bufParams.getQuadrantSegments();
105116           if (bufParams.getQuadrantSegments() >= 8 && bufParams.getJoinStyle() === BufferParameters.JOIN_ROUND) { this._closingSegLengthFactor = OffsetSegmentGenerator.MAX_CLOSING_SEG_LEN_FACTOR; }
105117           this.init(distance);
105118         };
105119
105120         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 } };
105121         OffsetSegmentGenerator.prototype.addNextSegment = function addNextSegment (p, addStartPoint) {
105122           this._s0 = this._s1;
105123           this._s1 = this._s2;
105124           this._s2 = p;
105125           this._seg0.setCoordinates(this._s0, this._s1);
105126           this.computeOffsetSegment(this._seg0, this._side, this._distance, this._offset0);
105127           this._seg1.setCoordinates(this._s1, this._s2);
105128           this.computeOffsetSegment(this._seg1, this._side, this._distance, this._offset1);
105129           if (this._s1.equals(this._s2)) { return null }
105130           var orientation = CGAlgorithms.computeOrientation(this._s0, this._s1, this._s2);
105131           var outsideTurn = (orientation === CGAlgorithms.CLOCKWISE && this._side === Position.LEFT) || (orientation === CGAlgorithms.COUNTERCLOCKWISE && this._side === Position.RIGHT);
105132           if (orientation === 0) {
105133             this.addCollinear(addStartPoint);
105134           } else if (outsideTurn) {
105135             this.addOutsideTurn(orientation, addStartPoint);
105136           } else {
105137             this.addInsideTurn(orientation, addStartPoint);
105138           }
105139         };
105140         OffsetSegmentGenerator.prototype.addLineEndCap = function addLineEndCap (p0, p1) {
105141           var seg = new LineSegment(p0, p1);
105142           var offsetL = new LineSegment();
105143           this.computeOffsetSegment(seg, Position.LEFT, this._distance, offsetL);
105144           var offsetR = new LineSegment();
105145           this.computeOffsetSegment(seg, Position.RIGHT, this._distance, offsetR);
105146           var dx = p1.x - p0.x;
105147           var dy = p1.y - p0.y;
105148           var angle = Math.atan2(dy, dx);
105149           switch (this._bufParams.getEndCapStyle()) {
105150             case BufferParameters.CAP_ROUND:
105151               this._segList.addPt(offsetL.p1);
105152               this.addFilletArc(p1, angle + Math.PI / 2, angle - Math.PI / 2, CGAlgorithms.CLOCKWISE, this._distance);
105153               this._segList.addPt(offsetR.p1);
105154               break
105155             case BufferParameters.CAP_FLAT:
105156               this._segList.addPt(offsetL.p1);
105157               this._segList.addPt(offsetR.p1);
105158               break
105159             case BufferParameters.CAP_SQUARE:
105160               var squareCapSideOffset = new Coordinate();
105161               squareCapSideOffset.x = Math.abs(this._distance) * Math.cos(angle);
105162               squareCapSideOffset.y = Math.abs(this._distance) * Math.sin(angle);
105163               var squareCapLOffset = new Coordinate(offsetL.p1.x + squareCapSideOffset.x, offsetL.p1.y + squareCapSideOffset.y);
105164               var squareCapROffset = new Coordinate(offsetR.p1.x + squareCapSideOffset.x, offsetR.p1.y + squareCapSideOffset.y);
105165               this._segList.addPt(squareCapLOffset);
105166               this._segList.addPt(squareCapROffset);
105167               break
105168           }
105169         };
105170         OffsetSegmentGenerator.prototype.getCoordinates = function getCoordinates () {
105171           var pts = this._segList.getCoordinates();
105172           return pts
105173         };
105174         OffsetSegmentGenerator.prototype.addMitreJoin = function addMitreJoin (p, offset0, offset1, distance) {
105175           var isMitreWithinLimit = true;
105176           var intPt = null;
105177           try {
105178             intPt = HCoordinate.intersection(offset0.p0, offset0.p1, offset1.p0, offset1.p1);
105179             var mitreRatio = distance <= 0.0 ? 1.0 : intPt.distance(p) / Math.abs(distance);
105180             if (mitreRatio > this._bufParams.getMitreLimit()) { isMitreWithinLimit = false; }
105181           } catch (ex) {
105182             if (ex instanceof NotRepresentableException) {
105183               intPt = new Coordinate(0, 0);
105184               isMitreWithinLimit = false;
105185             } else { throw ex }
105186           } finally {}
105187           if (isMitreWithinLimit) {
105188             this._segList.addPt(intPt);
105189           } else {
105190             this.addLimitedMitreJoin(offset0, offset1, distance, this._bufParams.getMitreLimit());
105191           }
105192         };
105193         OffsetSegmentGenerator.prototype.addFilletCorner = function addFilletCorner (p, p0, p1, direction, radius) {
105194           var dx0 = p0.x - p.x;
105195           var dy0 = p0.y - p.y;
105196           var startAngle = Math.atan2(dy0, dx0);
105197           var dx1 = p1.x - p.x;
105198           var dy1 = p1.y - p.y;
105199           var endAngle = Math.atan2(dy1, dx1);
105200           if (direction === CGAlgorithms.CLOCKWISE) {
105201             if (startAngle <= endAngle) { startAngle += 2.0 * Math.PI; }
105202           } else {
105203             if (startAngle >= endAngle) { startAngle -= 2.0 * Math.PI; }
105204           }
105205           this._segList.addPt(p0);
105206           this.addFilletArc(p, startAngle, endAngle, direction, radius);
105207           this._segList.addPt(p1);
105208         };
105209         OffsetSegmentGenerator.prototype.addOutsideTurn = function addOutsideTurn (orientation, addStartPoint) {
105210           if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.OFFSET_SEGMENT_SEPARATION_FACTOR) {
105211             this._segList.addPt(this._offset0.p1);
105212             return null
105213           }
105214           if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105215             this.addMitreJoin(this._s1, this._offset0, this._offset1, this._distance);
105216           } else if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL) {
105217             this.addBevelJoin(this._offset0, this._offset1);
105218           } else {
105219             if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105220             this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, orientation, this._distance);
105221             this._segList.addPt(this._offset1.p0);
105222           }
105223         };
105224         OffsetSegmentGenerator.prototype.createSquare = function createSquare (p) {
105225           this._segList.addPt(new Coordinate(p.x + this._distance, p.y + this._distance));
105226           this._segList.addPt(new Coordinate(p.x + this._distance, p.y - this._distance));
105227           this._segList.addPt(new Coordinate(p.x - this._distance, p.y - this._distance));
105228           this._segList.addPt(new Coordinate(p.x - this._distance, p.y + this._distance));
105229           this._segList.closeRing();
105230         };
105231         OffsetSegmentGenerator.prototype.addSegments = function addSegments (pt, isForward) {
105232           this._segList.addPts(pt, isForward);
105233         };
105234         OffsetSegmentGenerator.prototype.addFirstSegment = function addFirstSegment () {
105235           this._segList.addPt(this._offset1.p0);
105236         };
105237         OffsetSegmentGenerator.prototype.addLastSegment = function addLastSegment () {
105238           this._segList.addPt(this._offset1.p1);
105239         };
105240         OffsetSegmentGenerator.prototype.initSideSegments = function initSideSegments (s1, s2, side) {
105241           this._s1 = s1;
105242           this._s2 = s2;
105243           this._side = side;
105244           this._seg1.setCoordinates(s1, s2);
105245           this.computeOffsetSegment(this._seg1, side, this._distance, this._offset1);
105246         };
105247         OffsetSegmentGenerator.prototype.addLimitedMitreJoin = function addLimitedMitreJoin (offset0, offset1, distance, mitreLimit) {
105248           var basePt = this._seg0.p1;
105249           var ang0 = Angle.angle(basePt, this._seg0.p0);
105250           // const ang1 = Angle.angle(basePt, this._seg1.p1)
105251           var angDiff = Angle.angleBetweenOriented(this._seg0.p0, basePt, this._seg1.p1);
105252           var angDiffHalf = angDiff / 2;
105253           var midAng = Angle.normalize(ang0 + angDiffHalf);
105254           var mitreMidAng = Angle.normalize(midAng + Math.PI);
105255           var mitreDist = mitreLimit * distance;
105256           var bevelDelta = mitreDist * Math.abs(Math.sin(angDiffHalf));
105257           var bevelHalfLen = distance - bevelDelta;
105258           var bevelMidX = basePt.x + mitreDist * Math.cos(mitreMidAng);
105259           var bevelMidY = basePt.y + mitreDist * Math.sin(mitreMidAng);
105260           var bevelMidPt = new Coordinate(bevelMidX, bevelMidY);
105261           var mitreMidLine = new LineSegment(basePt, bevelMidPt);
105262           var bevelEndLeft = mitreMidLine.pointAlongOffset(1.0, bevelHalfLen);
105263           var bevelEndRight = mitreMidLine.pointAlongOffset(1.0, -bevelHalfLen);
105264           if (this._side === Position.LEFT) {
105265             this._segList.addPt(bevelEndLeft);
105266             this._segList.addPt(bevelEndRight);
105267           } else {
105268             this._segList.addPt(bevelEndRight);
105269             this._segList.addPt(bevelEndLeft);
105270           }
105271         };
105272         OffsetSegmentGenerator.prototype.computeOffsetSegment = function computeOffsetSegment (seg, side, distance, offset) {
105273           var sideSign = side === Position.LEFT ? 1 : -1;
105274           var dx = seg.p1.x - seg.p0.x;
105275           var dy = seg.p1.y - seg.p0.y;
105276           var len = Math.sqrt(dx * dx + dy * dy);
105277           var ux = sideSign * distance * dx / len;
105278           var uy = sideSign * distance * dy / len;
105279           offset.p0.x = seg.p0.x - uy;
105280           offset.p0.y = seg.p0.y + ux;
105281           offset.p1.x = seg.p1.x - uy;
105282           offset.p1.y = seg.p1.y + ux;
105283         };
105284         OffsetSegmentGenerator.prototype.addFilletArc = function addFilletArc (p, startAngle, endAngle, direction, radius) {
105285             var this$1 = this;
105286
105287           var directionFactor = direction === CGAlgorithms.CLOCKWISE ? -1 : 1;
105288           var totalAngle = Math.abs(startAngle - endAngle);
105289           var nSegs = Math.trunc(totalAngle / this._filletAngleQuantum + 0.5);
105290           if (nSegs < 1) { return null }
105291           var initAngle = 0.0;
105292           var currAngleInc = totalAngle / nSegs;
105293           var currAngle = initAngle;
105294           var pt = new Coordinate();
105295           while (currAngle < totalAngle) {
105296             var angle = startAngle + directionFactor * currAngle;
105297             pt.x = p.x + radius * Math.cos(angle);
105298             pt.y = p.y + radius * Math.sin(angle);
105299             this$1._segList.addPt(pt);
105300             currAngle += currAngleInc;
105301           }
105302         };
105303         OffsetSegmentGenerator.prototype.addInsideTurn = function addInsideTurn (orientation, addStartPoint) {
105304           this._li.computeIntersection(this._offset0.p0, this._offset0.p1, this._offset1.p0, this._offset1.p1);
105305           if (this._li.hasIntersection()) {
105306             this._segList.addPt(this._li.getIntersection(0));
105307           } else {
105308             this._hasNarrowConcaveAngle = true;
105309             if (this._offset0.p1.distance(this._offset1.p0) < this._distance * OffsetSegmentGenerator.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR) {
105310               this._segList.addPt(this._offset0.p1);
105311             } else {
105312               this._segList.addPt(this._offset0.p1);
105313               if (this._closingSegLengthFactor > 0) {
105314                 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));
105315                 this._segList.addPt(mid0);
105316                 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));
105317                 this._segList.addPt(mid1);
105318               } else {
105319                 this._segList.addPt(this._s1);
105320               }
105321               this._segList.addPt(this._offset1.p0);
105322             }
105323           }
105324         };
105325         OffsetSegmentGenerator.prototype.createCircle = function createCircle (p) {
105326           var pt = new Coordinate(p.x + this._distance, p.y);
105327           this._segList.addPt(pt);
105328           this.addFilletArc(p, 0.0, 2.0 * Math.PI, -1, this._distance);
105329           this._segList.closeRing();
105330         };
105331         OffsetSegmentGenerator.prototype.addBevelJoin = function addBevelJoin (offset0, offset1) {
105332           this._segList.addPt(offset0.p1);
105333           this._segList.addPt(offset1.p0);
105334         };
105335         OffsetSegmentGenerator.prototype.init = function init (distance) {
105336           this._distance = distance;
105337           this._maxCurveSegmentError = distance * (1 - Math.cos(this._filletAngleQuantum / 2.0));
105338           this._segList = new OffsetSegmentString();
105339           this._segList.setPrecisionModel(this._precisionModel);
105340           this._segList.setMinimumVertexDistance(distance * OffsetSegmentGenerator.CURVE_VERTEX_SNAP_DISTANCE_FACTOR);
105341         };
105342         OffsetSegmentGenerator.prototype.addCollinear = function addCollinear (addStartPoint) {
105343           this._li.computeIntersection(this._s0, this._s1, this._s1, this._s2);
105344           var numInt = this._li.getIntersectionNum();
105345           if (numInt >= 2) {
105346             if (this._bufParams.getJoinStyle() === BufferParameters.JOIN_BEVEL || this._bufParams.getJoinStyle() === BufferParameters.JOIN_MITRE) {
105347               if (addStartPoint) { this._segList.addPt(this._offset0.p1); }
105348               this._segList.addPt(this._offset1.p0);
105349             } else {
105350               this.addFilletCorner(this._s1, this._offset0.p1, this._offset1.p0, CGAlgorithms.CLOCKWISE, this._distance);
105351             }
105352           }
105353         };
105354         OffsetSegmentGenerator.prototype.closeRing = function closeRing () {
105355           this._segList.closeRing();
105356         };
105357         OffsetSegmentGenerator.prototype.hasNarrowConcaveAngle = function hasNarrowConcaveAngle () {
105358           return this._hasNarrowConcaveAngle
105359         };
105360         OffsetSegmentGenerator.prototype.interfaces_ = function interfaces_ () {
105361           return []
105362         };
105363         OffsetSegmentGenerator.prototype.getClass = function getClass () {
105364           return OffsetSegmentGenerator
105365         };
105366         staticAccessors$27.OFFSET_SEGMENT_SEPARATION_FACTOR.get = function () { return 1.0E-3 };
105367         staticAccessors$27.INSIDE_TURN_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-3 };
105368         staticAccessors$27.CURVE_VERTEX_SNAP_DISTANCE_FACTOR.get = function () { return 1.0E-6 };
105369         staticAccessors$27.MAX_CLOSING_SEG_LEN_FACTOR.get = function () { return 80 };
105370
105371         Object.defineProperties( OffsetSegmentGenerator, staticAccessors$27 );
105372
105373         var OffsetCurveBuilder = function OffsetCurveBuilder () {
105374           this._distance = 0.0;
105375           this._precisionModel = null;
105376           this._bufParams = null;
105377           var precisionModel = arguments[0];
105378           var bufParams = arguments[1];
105379           this._precisionModel = precisionModel;
105380           this._bufParams = bufParams;
105381         };
105382         OffsetCurveBuilder.prototype.getOffsetCurve = function getOffsetCurve (inputPts, distance) {
105383           this._distance = distance;
105384           if (distance === 0.0) { return null }
105385           var isRightSide = distance < 0.0;
105386           var posDistance = Math.abs(distance);
105387           var segGen = this.getSegGen(posDistance);
105388           if (inputPts.length <= 1) {
105389             this.computePointCurve(inputPts[0], segGen);
105390           } else {
105391             this.computeOffsetCurve(inputPts, isRightSide, segGen);
105392           }
105393           var curvePts = segGen.getCoordinates();
105394           if (isRightSide) { CoordinateArrays.reverse(curvePts); }
105395           return curvePts
105396         };
105397         OffsetCurveBuilder.prototype.computeSingleSidedBufferCurve = function computeSingleSidedBufferCurve (inputPts, isRightSide, segGen) {
105398           var distTol = this.simplifyTolerance(this._distance);
105399           if (isRightSide) {
105400             segGen.addSegments(inputPts, true);
105401             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105402             var n2 = simp2.length - 1;
105403             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105404             segGen.addFirstSegment();
105405             for (var i = n2 - 2; i >= 0; i--) {
105406               segGen.addNextSegment(simp2[i], true);
105407             }
105408           } else {
105409             segGen.addSegments(inputPts, false);
105410             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105411             var n1 = simp1.length - 1;
105412             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105413             segGen.addFirstSegment();
105414             for (var i$1 = 2; i$1 <= n1; i$1++) {
105415               segGen.addNextSegment(simp1[i$1], true);
105416             }
105417           }
105418           segGen.addLastSegment();
105419           segGen.closeRing();
105420         };
105421         OffsetCurveBuilder.prototype.computeRingBufferCurve = function computeRingBufferCurve (inputPts, side, segGen) {
105422           var distTol = this.simplifyTolerance(this._distance);
105423           if (side === Position.RIGHT) { distTol = -distTol; }
105424           var simp = BufferInputLineSimplifier.simplify(inputPts, distTol);
105425           var n = simp.length - 1;
105426           segGen.initSideSegments(simp[n - 1], simp[0], side);
105427           for (var i = 1; i <= n; i++) {
105428             var addStartPoint = i !== 1;
105429             segGen.addNextSegment(simp[i], addStartPoint);
105430           }
105431           segGen.closeRing();
105432         };
105433         OffsetCurveBuilder.prototype.computeLineBufferCurve = function computeLineBufferCurve (inputPts, segGen) {
105434           var distTol = this.simplifyTolerance(this._distance);
105435           var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105436           var n1 = simp1.length - 1;
105437           segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105438           for (var i = 2; i <= n1; i++) {
105439             segGen.addNextSegment(simp1[i], true);
105440           }
105441           segGen.addLastSegment();
105442           segGen.addLineEndCap(simp1[n1 - 1], simp1[n1]);
105443           var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105444           var n2 = simp2.length - 1;
105445           segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105446           for (var i$1 = n2 - 2; i$1 >= 0; i$1--) {
105447             segGen.addNextSegment(simp2[i$1], true);
105448           }
105449           segGen.addLastSegment();
105450           segGen.addLineEndCap(simp2[1], simp2[0]);
105451           segGen.closeRing();
105452         };
105453         OffsetCurveBuilder.prototype.computePointCurve = function computePointCurve (pt, segGen) {
105454           switch (this._bufParams.getEndCapStyle()) {
105455             case BufferParameters.CAP_ROUND:
105456               segGen.createCircle(pt);
105457               break
105458             case BufferParameters.CAP_SQUARE:
105459               segGen.createSquare(pt);
105460               break
105461           }
105462         };
105463         OffsetCurveBuilder.prototype.getLineCurve = function getLineCurve (inputPts, distance) {
105464           this._distance = distance;
105465           if (distance < 0.0 && !this._bufParams.isSingleSided()) { return null }
105466           if (distance === 0.0) { return null }
105467           var posDistance = Math.abs(distance);
105468           var segGen = this.getSegGen(posDistance);
105469           if (inputPts.length <= 1) {
105470             this.computePointCurve(inputPts[0], segGen);
105471           } else {
105472             if (this._bufParams.isSingleSided()) {
105473               var isRightSide = distance < 0.0;
105474               this.computeSingleSidedBufferCurve(inputPts, isRightSide, segGen);
105475             } else { this.computeLineBufferCurve(inputPts, segGen); }
105476           }
105477           var lineCoord = segGen.getCoordinates();
105478           return lineCoord
105479         };
105480         OffsetCurveBuilder.prototype.getBufferParameters = function getBufferParameters () {
105481           return this._bufParams
105482         };
105483         OffsetCurveBuilder.prototype.simplifyTolerance = function simplifyTolerance (bufDistance) {
105484           return bufDistance * this._bufParams.getSimplifyFactor()
105485         };
105486         OffsetCurveBuilder.prototype.getRingCurve = function getRingCurve (inputPts, side, distance) {
105487           this._distance = distance;
105488           if (inputPts.length <= 2) { return this.getLineCurve(inputPts, distance) }
105489           if (distance === 0.0) {
105490             return OffsetCurveBuilder.copyCoordinates(inputPts)
105491           }
105492           var segGen = this.getSegGen(distance);
105493           this.computeRingBufferCurve(inputPts, side, segGen);
105494           return segGen.getCoordinates()
105495         };
105496         OffsetCurveBuilder.prototype.computeOffsetCurve = function computeOffsetCurve (inputPts, isRightSide, segGen) {
105497           var distTol = this.simplifyTolerance(this._distance);
105498           if (isRightSide) {
105499             var simp2 = BufferInputLineSimplifier.simplify(inputPts, -distTol);
105500             var n2 = simp2.length - 1;
105501             segGen.initSideSegments(simp2[n2], simp2[n2 - 1], Position.LEFT);
105502             segGen.addFirstSegment();
105503             for (var i = n2 - 2; i >= 0; i--) {
105504               segGen.addNextSegment(simp2[i], true);
105505             }
105506           } else {
105507             var simp1 = BufferInputLineSimplifier.simplify(inputPts, distTol);
105508             var n1 = simp1.length - 1;
105509             segGen.initSideSegments(simp1[0], simp1[1], Position.LEFT);
105510             segGen.addFirstSegment();
105511             for (var i$1 = 2; i$1 <= n1; i$1++) {
105512               segGen.addNextSegment(simp1[i$1], true);
105513             }
105514           }
105515           segGen.addLastSegment();
105516         };
105517         OffsetCurveBuilder.prototype.getSegGen = function getSegGen (distance) {
105518           return new OffsetSegmentGenerator(this._precisionModel, this._bufParams, distance)
105519         };
105520         OffsetCurveBuilder.prototype.interfaces_ = function interfaces_ () {
105521           return []
105522         };
105523         OffsetCurveBuilder.prototype.getClass = function getClass () {
105524           return OffsetCurveBuilder
105525         };
105526         OffsetCurveBuilder.copyCoordinates = function copyCoordinates (pts) {
105527           var copy = new Array(pts.length).fill(null);
105528           for (var i = 0; i < copy.length; i++) {
105529             copy[i] = new Coordinate(pts[i]);
105530           }
105531           return copy
105532         };
105533
105534         var SubgraphDepthLocater = function SubgraphDepthLocater () {
105535           this._subgraphs = null;
105536           this._seg = new LineSegment();
105537           this._cga = new CGAlgorithms();
105538           var subgraphs = arguments[0];
105539           this._subgraphs = subgraphs;
105540         };
105541
105542         var staticAccessors$30 = { DepthSegment: { configurable: true } };
105543         SubgraphDepthLocater.prototype.findStabbedSegments = function findStabbedSegments () {
105544             var this$1 = this;
105545
105546           if (arguments.length === 1) {
105547             var stabbingRayLeftPt = arguments[0];
105548             var stabbedSegments = new ArrayList();
105549             for (var i = this._subgraphs.iterator(); i.hasNext();) {
105550               var bsg = i.next();
105551               var env = bsg.getEnvelope();
105552               if (stabbingRayLeftPt.y < env.getMinY() || stabbingRayLeftPt.y > env.getMaxY()) { continue }
105553               this$1.findStabbedSegments(stabbingRayLeftPt, bsg.getDirectedEdges(), stabbedSegments);
105554             }
105555             return stabbedSegments
105556           } else if (arguments.length === 3) {
105557             if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && arguments[1] instanceof DirectedEdge)) {
105558               var stabbingRayLeftPt$1 = arguments[0];
105559               var dirEdge = arguments[1];
105560               var stabbedSegments$1 = arguments[2];
105561               var pts = dirEdge.getEdge().getCoordinates();
105562               for (var i$1 = 0; i$1 < pts.length - 1; i$1++) {
105563                 this$1._seg.p0 = pts[i$1];
105564                 this$1._seg.p1 = pts[i$1 + 1];
105565                 if (this$1._seg.p0.y > this$1._seg.p1.y) { this$1._seg.reverse(); }
105566                 var maxx = Math.max(this$1._seg.p0.x, this$1._seg.p1.x);
105567                 if (maxx < stabbingRayLeftPt$1.x) { continue }
105568                 if (this$1._seg.isHorizontal()) { continue }
105569                 if (stabbingRayLeftPt$1.y < this$1._seg.p0.y || stabbingRayLeftPt$1.y > this$1._seg.p1.y) { continue }
105570                 if (CGAlgorithms.computeOrientation(this$1._seg.p0, this$1._seg.p1, stabbingRayLeftPt$1) === CGAlgorithms.RIGHT) { continue }
105571                 var depth = dirEdge.getDepth(Position.LEFT);
105572                 if (!this$1._seg.p0.equals(pts[i$1])) { depth = dirEdge.getDepth(Position.RIGHT); }
105573                 var ds = new DepthSegment(this$1._seg, depth);
105574                 stabbedSegments$1.add(ds);
105575               }
105576             } else if (hasInterface(arguments[2], List) && (arguments[0] instanceof Coordinate && hasInterface(arguments[1], List))) {
105577               var stabbingRayLeftPt$2 = arguments[0];
105578               var dirEdges = arguments[1];
105579               var stabbedSegments$2 = arguments[2];
105580               for (var i$2 = dirEdges.iterator(); i$2.hasNext();) {
105581                 var de = i$2.next();
105582                 if (!de.isForward()) { continue }
105583                 this$1.findStabbedSegments(stabbingRayLeftPt$2, de, stabbedSegments$2);
105584               }
105585             }
105586           }
105587         };
105588         SubgraphDepthLocater.prototype.getDepth = function getDepth (p) {
105589           var stabbedSegments = this.findStabbedSegments(p);
105590           if (stabbedSegments.size() === 0) { return 0 }
105591           var ds = Collections.min(stabbedSegments);
105592           return ds._leftDepth
105593         };
105594         SubgraphDepthLocater.prototype.interfaces_ = function interfaces_ () {
105595           return []
105596         };
105597         SubgraphDepthLocater.prototype.getClass = function getClass () {
105598           return SubgraphDepthLocater
105599         };
105600         staticAccessors$30.DepthSegment.get = function () { return DepthSegment };
105601
105602         Object.defineProperties( SubgraphDepthLocater, staticAccessors$30 );
105603
105604         var DepthSegment = function DepthSegment () {
105605           this._upwardSeg = null;
105606           this._leftDepth = null;
105607           var seg = arguments[0];
105608           var depth = arguments[1];
105609           this._upwardSeg = new LineSegment(seg);
105610           this._leftDepth = depth;
105611         };
105612         DepthSegment.prototype.compareTo = function compareTo (obj) {
105613           var other = obj;
105614           if (this._upwardSeg.minX() >= other._upwardSeg.maxX()) { return 1 }
105615           if (this._upwardSeg.maxX() <= other._upwardSeg.minX()) { return -1 }
105616           var orientIndex = this._upwardSeg.orientationIndex(other._upwardSeg);
105617           if (orientIndex !== 0) { return orientIndex }
105618           orientIndex = -1 * other._upwardSeg.orientationIndex(this._upwardSeg);
105619           if (orientIndex !== 0) { return orientIndex }
105620           return this._upwardSeg.compareTo(other._upwardSeg)
105621         };
105622         DepthSegment.prototype.compareX = function compareX (seg0, seg1) {
105623           var compare0 = seg0.p0.compareTo(seg1.p0);
105624           if (compare0 !== 0) { return compare0 }
105625           return seg0.p1.compareTo(seg1.p1)
105626         };
105627         DepthSegment.prototype.toString = function toString () {
105628           return this._upwardSeg.toString()
105629         };
105630         DepthSegment.prototype.interfaces_ = function interfaces_ () {
105631           return [Comparable]
105632         };
105633         DepthSegment.prototype.getClass = function getClass () {
105634           return DepthSegment
105635         };
105636
105637         var Triangle = function Triangle (p0, p1, p2) {
105638           this.p0 = p0 || null;
105639           this.p1 = p1 || null;
105640           this.p2 = p2 || null;
105641         };
105642         Triangle.prototype.area = function area () {
105643           return Triangle.area(this.p0, this.p1, this.p2)
105644         };
105645         Triangle.prototype.signedArea = function signedArea () {
105646           return Triangle.signedArea(this.p0, this.p1, this.p2)
105647         };
105648         Triangle.prototype.interpolateZ = function interpolateZ (p) {
105649           if (p === null) { throw new IllegalArgumentException('Supplied point is null.') }
105650           return Triangle.interpolateZ(p, this.p0, this.p1, this.p2)
105651         };
105652         Triangle.prototype.longestSideLength = function longestSideLength () {
105653           return Triangle.longestSideLength(this.p0, this.p1, this.p2)
105654         };
105655         Triangle.prototype.isAcute = function isAcute () {
105656           return Triangle.isAcute(this.p0, this.p1, this.p2)
105657         };
105658         Triangle.prototype.circumcentre = function circumcentre () {
105659           return Triangle.circumcentre(this.p0, this.p1, this.p2)
105660         };
105661         Triangle.prototype.area3D = function area3D () {
105662           return Triangle.area3D(this.p0, this.p1, this.p2)
105663         };
105664         Triangle.prototype.centroid = function centroid () {
105665           return Triangle.centroid(this.p0, this.p1, this.p2)
105666         };
105667         Triangle.prototype.inCentre = function inCentre () {
105668           return Triangle.inCentre(this.p0, this.p1, this.p2)
105669         };
105670         Triangle.prototype.interfaces_ = function interfaces_ () {
105671           return []
105672         };
105673         Triangle.prototype.getClass = function getClass () {
105674           return Triangle
105675         };
105676         Triangle.area = function area (a, b, c) {
105677           return Math.abs(((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2)
105678         };
105679         Triangle.signedArea = function signedArea (a, b, c) {
105680           return ((c.x - a.x) * (b.y - a.y) - (b.x - a.x) * (c.y - a.y)) / 2
105681         };
105682         Triangle.det = function det (m00, m01, m10, m11) {
105683           return m00 * m11 - m01 * m10
105684         };
105685         Triangle.interpolateZ = function interpolateZ (p, v0, v1, v2) {
105686           var x0 = v0.x;
105687           var y0 = v0.y;
105688           var a = v1.x - x0;
105689           var b = v2.x - x0;
105690           var c = v1.y - y0;
105691           var d = v2.y - y0;
105692           var det = a * d - b * c;
105693           var dx = p.x - x0;
105694           var dy = p.y - y0;
105695           var t = (d * dx - b * dy) / det;
105696           var u = (-c * dx + a * dy) / det;
105697           var z = v0.z + t * (v1.z - v0.z) + u * (v2.z - v0.z);
105698           return z
105699         };
105700         Triangle.longestSideLength = function longestSideLength (a, b, c) {
105701           var lenAB = a.distance(b);
105702           var lenBC = b.distance(c);
105703           var lenCA = c.distance(a);
105704           var maxLen = lenAB;
105705           if (lenBC > maxLen) { maxLen = lenBC; }
105706           if (lenCA > maxLen) { maxLen = lenCA; }
105707           return maxLen
105708         };
105709         Triangle.isAcute = function isAcute (a, b, c) {
105710           if (!Angle.isAcute(a, b, c)) { return false }
105711           if (!Angle.isAcute(b, c, a)) { return false }
105712           if (!Angle.isAcute(c, a, b)) { return false }
105713           return true
105714         };
105715         Triangle.circumcentre = function circumcentre (a, b, c) {
105716           var cx = c.x;
105717           var cy = c.y;
105718           var ax = a.x - cx;
105719           var ay = a.y - cy;
105720           var bx = b.x - cx;
105721           var by = b.y - cy;
105722           var denom = 2 * Triangle.det(ax, ay, bx, by);
105723           var numx = Triangle.det(ay, ax * ax + ay * ay, by, bx * bx + by * by);
105724           var numy = Triangle.det(ax, ax * ax + ay * ay, bx, bx * bx + by * by);
105725           var ccx = cx - numx / denom;
105726           var ccy = cy + numy / denom;
105727           return new Coordinate(ccx, ccy)
105728         };
105729         Triangle.perpendicularBisector = function perpendicularBisector (a, b) {
105730           var dx = b.x - a.x;
105731           var dy = b.y - a.y;
105732           var l1 = new HCoordinate(a.x + dx / 2.0, a.y + dy / 2.0, 1.0);
105733           var l2 = new HCoordinate(a.x - dy + dx / 2.0, a.y + dx + dy / 2.0, 1.0);
105734           return new HCoordinate(l1, l2)
105735         };
105736         Triangle.angleBisector = function angleBisector (a, b, c) {
105737           var len0 = b.distance(a);
105738           var len2 = b.distance(c);
105739           var frac = len0 / (len0 + len2);
105740           var dx = c.x - a.x;
105741           var dy = c.y - a.y;
105742           var splitPt = new Coordinate(a.x + frac * dx, a.y + frac * dy);
105743           return splitPt
105744         };
105745         Triangle.area3D = function area3D (a, b, c) {
105746           var ux = b.x - a.x;
105747           var uy = b.y - a.y;
105748           var uz = b.z - a.z;
105749           var vx = c.x - a.x;
105750           var vy = c.y - a.y;
105751           var vz = c.z - a.z;
105752           var crossx = uy * vz - uz * vy;
105753           var crossy = uz * vx - ux * vz;
105754           var crossz = ux * vy - uy * vx;
105755           var absSq = crossx * crossx + crossy * crossy + crossz * crossz;
105756           var area3D = Math.sqrt(absSq) / 2;
105757           return area3D
105758         };
105759         Triangle.centroid = function centroid (a, b, c) {
105760           var x = (a.x + b.x + c.x) / 3;
105761           var y = (a.y + b.y + c.y) / 3;
105762           return new Coordinate(x, y)
105763         };
105764         Triangle.inCentre = function inCentre (a, b, c) {
105765           var len0 = b.distance(c);
105766           var len1 = a.distance(c);
105767           var len2 = a.distance(b);
105768           var circum = len0 + len1 + len2;
105769           var inCentreX = (len0 * a.x + len1 * b.x + len2 * c.x) / circum;
105770           var inCentreY = (len0 * a.y + len1 * b.y + len2 * c.y) / circum;
105771           return new Coordinate(inCentreX, inCentreY)
105772         };
105773
105774         var OffsetCurveSetBuilder = function OffsetCurveSetBuilder () {
105775           this._inputGeom = null;
105776           this._distance = null;
105777           this._curveBuilder = null;
105778           this._curveList = new ArrayList();
105779           var inputGeom = arguments[0];
105780           var distance = arguments[1];
105781           var curveBuilder = arguments[2];
105782           this._inputGeom = inputGeom;
105783           this._distance = distance;
105784           this._curveBuilder = curveBuilder;
105785         };
105786         OffsetCurveSetBuilder.prototype.addPoint = function addPoint (p) {
105787           if (this._distance <= 0.0) { return null }
105788           var coord = p.getCoordinates();
105789           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105790           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105791         };
105792         OffsetCurveSetBuilder.prototype.addPolygon = function addPolygon (p) {
105793             var this$1 = this;
105794
105795           var offsetDistance = this._distance;
105796           var offsetSide = Position.LEFT;
105797           if (this._distance < 0.0) {
105798             offsetDistance = -this._distance;
105799             offsetSide = Position.RIGHT;
105800           }
105801           var shell = p.getExteriorRing();
105802           var shellCoord = CoordinateArrays.removeRepeatedPoints(shell.getCoordinates());
105803           if (this._distance < 0.0 && this.isErodedCompletely(shell, this._distance)) { return null }
105804           if (this._distance <= 0.0 && shellCoord.length < 3) { return null }
105805           this.addPolygonRing(shellCoord, offsetDistance, offsetSide, Location.EXTERIOR, Location.INTERIOR);
105806           for (var i = 0; i < p.getNumInteriorRing(); i++) {
105807             var hole = p.getInteriorRingN(i);
105808             var holeCoord = CoordinateArrays.removeRepeatedPoints(hole.getCoordinates());
105809             if (this$1._distance > 0.0 && this$1.isErodedCompletely(hole, -this$1._distance)) { continue }
105810             this$1.addPolygonRing(holeCoord, offsetDistance, Position.opposite(offsetSide), Location.INTERIOR, Location.EXTERIOR);
105811           }
105812         };
105813         OffsetCurveSetBuilder.prototype.isTriangleErodedCompletely = function isTriangleErodedCompletely (triangleCoord, bufferDistance) {
105814           var tri = new Triangle(triangleCoord[0], triangleCoord[1], triangleCoord[2]);
105815           var inCentre = tri.inCentre();
105816           var distToCentre = CGAlgorithms.distancePointLine(inCentre, tri.p0, tri.p1);
105817           return distToCentre < Math.abs(bufferDistance)
105818         };
105819         OffsetCurveSetBuilder.prototype.addLineString = function addLineString (line) {
105820           if (this._distance <= 0.0 && !this._curveBuilder.getBufferParameters().isSingleSided()) { return null }
105821           var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
105822           var curve = this._curveBuilder.getLineCurve(coord, this._distance);
105823           this.addCurve(curve, Location.EXTERIOR, Location.INTERIOR);
105824         };
105825         OffsetCurveSetBuilder.prototype.addCurve = function addCurve (coord, leftLoc, rightLoc) {
105826           if (coord === null || coord.length < 2) { return null }
105827           var e = new NodedSegmentString(coord, new Label(0, Location.BOUNDARY, leftLoc, rightLoc));
105828           this._curveList.add(e);
105829         };
105830         OffsetCurveSetBuilder.prototype.getCurves = function getCurves () {
105831           this.add(this._inputGeom);
105832           return this._curveList
105833         };
105834         OffsetCurveSetBuilder.prototype.addPolygonRing = function addPolygonRing (coord, offsetDistance, side, cwLeftLoc, cwRightLoc) {
105835           if (offsetDistance === 0.0 && coord.length < LinearRing.MINIMUM_VALID_SIZE) { return null }
105836           var leftLoc = cwLeftLoc;
105837           var rightLoc = cwRightLoc;
105838           if (coord.length >= LinearRing.MINIMUM_VALID_SIZE && CGAlgorithms.isCCW(coord)) {
105839             leftLoc = cwRightLoc;
105840             rightLoc = cwLeftLoc;
105841             side = Position.opposite(side);
105842           }
105843           var curve = this._curveBuilder.getRingCurve(coord, side, offsetDistance);
105844           this.addCurve(curve, leftLoc, rightLoc);
105845         };
105846         OffsetCurveSetBuilder.prototype.add = function add (g) {
105847           if (g.isEmpty()) { return null }
105848           if (g instanceof Polygon) { this.addPolygon(g); }
105849           else if (g instanceof LineString) { this.addLineString(g); }
105850           else if (g instanceof Point$1) { this.addPoint(g); }
105851           else if (g instanceof MultiPoint) { this.addCollection(g); }
105852           else if (g instanceof MultiLineString) { this.addCollection(g); }
105853           else if (g instanceof MultiPolygon) { this.addCollection(g); }
105854           else if (g instanceof GeometryCollection) { this.addCollection(g); }
105855           // else throw new UnsupportedOperationException(g.getClass().getName())
105856         };
105857         OffsetCurveSetBuilder.prototype.isErodedCompletely = function isErodedCompletely (ring, bufferDistance) {
105858           var ringCoord = ring.getCoordinates();
105859           // const minDiam = 0.0
105860           if (ringCoord.length < 4) { return bufferDistance < 0 }
105861           if (ringCoord.length === 4) { return this.isTriangleErodedCompletely(ringCoord, bufferDistance) }
105862           var env = ring.getEnvelopeInternal();
105863           var envMinDimension = Math.min(env.getHeight(), env.getWidth());
105864           if (bufferDistance < 0.0 && 2 * Math.abs(bufferDistance) > envMinDimension) { return true }
105865           return false
105866         };
105867         OffsetCurveSetBuilder.prototype.addCollection = function addCollection (gc) {
105868             var this$1 = this;
105869
105870           for (var i = 0; i < gc.getNumGeometries(); i++) {
105871             var g = gc.getGeometryN(i);
105872             this$1.add(g);
105873           }
105874         };
105875         OffsetCurveSetBuilder.prototype.interfaces_ = function interfaces_ () {
105876           return []
105877         };
105878         OffsetCurveSetBuilder.prototype.getClass = function getClass () {
105879           return OffsetCurveSetBuilder
105880         };
105881
105882         var PointOnGeometryLocator = function PointOnGeometryLocator () {};
105883
105884         PointOnGeometryLocator.prototype.locate = function locate (p) {};
105885         PointOnGeometryLocator.prototype.interfaces_ = function interfaces_ () {
105886           return []
105887         };
105888         PointOnGeometryLocator.prototype.getClass = function getClass () {
105889           return PointOnGeometryLocator
105890         };
105891
105892         var GeometryCollectionIterator = function GeometryCollectionIterator () {
105893           this._parent = null;
105894           this._atStart = null;
105895           this._max = null;
105896           this._index = null;
105897           this._subcollectionIterator = null;
105898           var parent = arguments[0];
105899           this._parent = parent;
105900           this._atStart = true;
105901           this._index = 0;
105902           this._max = parent.getNumGeometries();
105903         };
105904         GeometryCollectionIterator.prototype.next = function next () {
105905           if (this._atStart) {
105906             this._atStart = false;
105907             if (GeometryCollectionIterator.isAtomic(this._parent)) { this._index++; }
105908             return this._parent
105909           }
105910           if (this._subcollectionIterator !== null) {
105911             if (this._subcollectionIterator.hasNext()) {
105912               return this._subcollectionIterator.next()
105913             } else {
105914               this._subcollectionIterator = null;
105915             }
105916           }
105917           if (this._index >= this._max) {
105918             throw new NoSuchElementException()
105919           }
105920           var obj = this._parent.getGeometryN(this._index++);
105921           if (obj instanceof GeometryCollection) {
105922             this._subcollectionIterator = new GeometryCollectionIterator(obj);
105923             return this._subcollectionIterator.next()
105924           }
105925           return obj
105926         };
105927         GeometryCollectionIterator.prototype.remove = function remove () {
105928           throw new Error(this.getClass().getName())
105929         };
105930         GeometryCollectionIterator.prototype.hasNext = function hasNext () {
105931           if (this._atStart) {
105932             return true
105933           }
105934           if (this._subcollectionIterator !== null) {
105935             if (this._subcollectionIterator.hasNext()) {
105936               return true
105937             }
105938             this._subcollectionIterator = null;
105939           }
105940           if (this._index >= this._max) {
105941             return false
105942           }
105943           return true
105944         };
105945         GeometryCollectionIterator.prototype.interfaces_ = function interfaces_ () {
105946           return [Iterator$1]
105947         };
105948         GeometryCollectionIterator.prototype.getClass = function getClass () {
105949           return GeometryCollectionIterator
105950         };
105951         GeometryCollectionIterator.isAtomic = function isAtomic (geom) {
105952           return !(geom instanceof GeometryCollection)
105953         };
105954
105955         var SimplePointInAreaLocator = function SimplePointInAreaLocator () {
105956           this._geom = null;
105957           var geom = arguments[0];
105958           this._geom = geom;
105959         };
105960         SimplePointInAreaLocator.prototype.locate = function locate (p) {
105961           return SimplePointInAreaLocator.locate(p, this._geom)
105962         };
105963         SimplePointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
105964           return [PointOnGeometryLocator]
105965         };
105966         SimplePointInAreaLocator.prototype.getClass = function getClass () {
105967           return SimplePointInAreaLocator
105968         };
105969         SimplePointInAreaLocator.isPointInRing = function isPointInRing (p, ring) {
105970           if (!ring.getEnvelopeInternal().intersects(p)) { return false }
105971           return CGAlgorithms.isPointInRing(p, ring.getCoordinates())
105972         };
105973         SimplePointInAreaLocator.containsPointInPolygon = function containsPointInPolygon (p, poly) {
105974           if (poly.isEmpty()) { return false }
105975           var shell = poly.getExteriorRing();
105976           if (!SimplePointInAreaLocator.isPointInRing(p, shell)) { return false }
105977           for (var i = 0; i < poly.getNumInteriorRing(); i++) {
105978             var hole = poly.getInteriorRingN(i);
105979             if (SimplePointInAreaLocator.isPointInRing(p, hole)) { return false }
105980           }
105981           return true
105982         };
105983         SimplePointInAreaLocator.containsPoint = function containsPoint (p, geom) {
105984           if (geom instanceof Polygon) {
105985             return SimplePointInAreaLocator.containsPointInPolygon(p, geom)
105986           } else if (geom instanceof GeometryCollection) {
105987             var geomi = new GeometryCollectionIterator(geom);
105988             while (geomi.hasNext()) {
105989               var g2 = geomi.next();
105990               if (g2 !== geom) { if (SimplePointInAreaLocator.containsPoint(p, g2)) { return true } }
105991             }
105992           }
105993           return false
105994         };
105995         SimplePointInAreaLocator.locate = function locate (p, geom) {
105996           if (geom.isEmpty()) { return Location.EXTERIOR }
105997           if (SimplePointInAreaLocator.containsPoint(p, geom)) { return Location.INTERIOR }
105998           return Location.EXTERIOR
105999         };
106000
106001         var EdgeEndStar = function EdgeEndStar () {
106002           this._edgeMap = new TreeMap();
106003           this._edgeList = null;
106004           this._ptInAreaLocation = [Location.NONE, Location.NONE];
106005         };
106006         EdgeEndStar.prototype.getNextCW = function getNextCW (ee) {
106007           this.getEdges();
106008           var i = this._edgeList.indexOf(ee);
106009           var iNextCW = i - 1;
106010           if (i === 0) { iNextCW = this._edgeList.size() - 1; }
106011           return this._edgeList.get(iNextCW)
106012         };
106013         EdgeEndStar.prototype.propagateSideLabels = function propagateSideLabels (geomIndex) {
106014           var startLoc = Location.NONE;
106015           for (var it = this.iterator(); it.hasNext();) {
106016             var e = it.next();
106017             var label = e.getLabel();
106018             if (label.isArea(geomIndex) && label.getLocation(geomIndex, Position.LEFT) !== Location.NONE) { startLoc = label.getLocation(geomIndex, Position.LEFT); }
106019           }
106020           if (startLoc === Location.NONE) { return null }
106021           var currLoc = startLoc;
106022           for (var it$1 = this.iterator(); it$1.hasNext();) {
106023             var e$1 = it$1.next();
106024             var label$1 = e$1.getLabel();
106025             if (label$1.getLocation(geomIndex, Position.ON) === Location.NONE) { label$1.setLocation(geomIndex, Position.ON, currLoc); }
106026             if (label$1.isArea(geomIndex)) {
106027               var leftLoc = label$1.getLocation(geomIndex, Position.LEFT);
106028               var rightLoc = label$1.getLocation(geomIndex, Position.RIGHT);
106029               if (rightLoc !== Location.NONE) {
106030                 if (rightLoc !== currLoc) { throw new TopologyException('side location conflict', e$1.getCoordinate()) }
106031                 if (leftLoc === Location.NONE) {
106032                   Assert.shouldNeverReachHere('found single null side (at ' + e$1.getCoordinate() + ')');
106033                 }
106034                 currLoc = leftLoc;
106035               } else {
106036                 Assert.isTrue(label$1.getLocation(geomIndex, Position.LEFT) === Location.NONE, 'found single null side');
106037                 label$1.setLocation(geomIndex, Position.RIGHT, currLoc);
106038                 label$1.setLocation(geomIndex, Position.LEFT, currLoc);
106039               }
106040             }
106041           }
106042         };
106043         EdgeEndStar.prototype.getCoordinate = function getCoordinate () {
106044           var it = this.iterator();
106045           if (!it.hasNext()) { return null }
106046           var e = it.next();
106047           return e.getCoordinate()
106048         };
106049         EdgeEndStar.prototype.print = function print (out) {
106050           System.out.println('EdgeEndStar:   ' + this.getCoordinate());
106051           for (var it = this.iterator(); it.hasNext();) {
106052             var e = it.next();
106053             e.print(out);
106054           }
106055         };
106056         EdgeEndStar.prototype.isAreaLabelsConsistent = function isAreaLabelsConsistent (geomGraph) {
106057           this.computeEdgeEndLabels(geomGraph.getBoundaryNodeRule());
106058           return this.checkAreaLabelsConsistent(0)
106059         };
106060         EdgeEndStar.prototype.checkAreaLabelsConsistent = function checkAreaLabelsConsistent (geomIndex) {
106061           var edges = this.getEdges();
106062           if (edges.size() <= 0) { return true }
106063           var lastEdgeIndex = edges.size() - 1;
106064           var startLabel = edges.get(lastEdgeIndex).getLabel();
106065           var startLoc = startLabel.getLocation(geomIndex, Position.LEFT);
106066           Assert.isTrue(startLoc !== Location.NONE, 'Found unlabelled area edge');
106067           var currLoc = startLoc;
106068           for (var it = this.iterator(); it.hasNext();) {
106069             var e = it.next();
106070             var label = e.getLabel();
106071             Assert.isTrue(label.isArea(geomIndex), 'Found non-area edge');
106072             var leftLoc = label.getLocation(geomIndex, Position.LEFT);
106073             var rightLoc = label.getLocation(geomIndex, Position.RIGHT);
106074             if (leftLoc === rightLoc) {
106075               return false
106076             }
106077             if (rightLoc !== currLoc) {
106078               return false
106079             }
106080             currLoc = leftLoc;
106081           }
106082           return true
106083         };
106084         EdgeEndStar.prototype.findIndex = function findIndex (eSearch) {
106085             var this$1 = this;
106086
106087           this.iterator();
106088           for (var i = 0; i < this._edgeList.size(); i++) {
106089             var e = this$1._edgeList.get(i);
106090             if (e === eSearch) { return i }
106091           }
106092           return -1
106093         };
106094         EdgeEndStar.prototype.iterator = function iterator () {
106095           return this.getEdges().iterator()
106096         };
106097         EdgeEndStar.prototype.getEdges = function getEdges () {
106098           if (this._edgeList === null) {
106099             this._edgeList = new ArrayList(this._edgeMap.values());
106100           }
106101           return this._edgeList
106102         };
106103         EdgeEndStar.prototype.getLocation = function getLocation (geomIndex, p, geom) {
106104           if (this._ptInAreaLocation[geomIndex] === Location.NONE) {
106105             this._ptInAreaLocation[geomIndex] = SimplePointInAreaLocator.locate(p, geom[geomIndex].getGeometry());
106106           }
106107           return this._ptInAreaLocation[geomIndex]
106108         };
106109         EdgeEndStar.prototype.toString = function toString () {
106110           var buf = new StringBuffer();
106111           buf.append('EdgeEndStar:   ' + this.getCoordinate());
106112           buf.append('\n');
106113           for (var it = this.iterator(); it.hasNext();) {
106114             var e = it.next();
106115             buf.append(e);
106116             buf.append('\n');
106117           }
106118           return buf.toString()
106119         };
106120         EdgeEndStar.prototype.computeEdgeEndLabels = function computeEdgeEndLabels (boundaryNodeRule) {
106121           for (var it = this.iterator(); it.hasNext();) {
106122             var ee = it.next();
106123             ee.computeLabel(boundaryNodeRule);
106124           }
106125         };
106126         EdgeEndStar.prototype.computeLabelling = function computeLabelling (geomGraph) {
106127             var this$1 = this;
106128
106129           this.computeEdgeEndLabels(geomGraph[0].getBoundaryNodeRule());
106130           this.propagateSideLabels(0);
106131           this.propagateSideLabels(1);
106132           var hasDimensionalCollapseEdge = [false, false];
106133           for (var it = this.iterator(); it.hasNext();) {
106134             var e = it.next();
106135             var label = e.getLabel();
106136             for (var geomi = 0; geomi < 2; geomi++) {
106137               if (label.isLine(geomi) && label.getLocation(geomi) === Location.BOUNDARY) { hasDimensionalCollapseEdge[geomi] = true; }
106138             }
106139           }
106140           for (var it$1 = this.iterator(); it$1.hasNext();) {
106141             var e$1 = it$1.next();
106142             var label$1 = e$1.getLabel();
106143             for (var geomi$1 = 0; geomi$1 < 2; geomi$1++) {
106144               if (label$1.isAnyNull(geomi$1)) {
106145                 var loc = Location.NONE;
106146                 if (hasDimensionalCollapseEdge[geomi$1]) {
106147                   loc = Location.EXTERIOR;
106148                 } else {
106149                   var p = e$1.getCoordinate();
106150                   loc = this$1.getLocation(geomi$1, p, geomGraph);
106151                 }
106152                 label$1.setAllLocationsIfNull(geomi$1, loc);
106153               }
106154             }
106155           }
106156         };
106157         EdgeEndStar.prototype.getDegree = function getDegree () {
106158           return this._edgeMap.size()
106159         };
106160         EdgeEndStar.prototype.insertEdgeEnd = function insertEdgeEnd (e, obj) {
106161           this._edgeMap.put(e, obj);
106162           this._edgeList = null;
106163         };
106164         EdgeEndStar.prototype.interfaces_ = function interfaces_ () {
106165           return []
106166         };
106167         EdgeEndStar.prototype.getClass = function getClass () {
106168           return EdgeEndStar
106169         };
106170
106171         var DirectedEdgeStar = (function (EdgeEndStar$$1) {
106172           function DirectedEdgeStar () {
106173             EdgeEndStar$$1.call(this);
106174             this._resultAreaEdgeList = null;
106175             this._label = null;
106176             this._SCANNING_FOR_INCOMING = 1;
106177             this._LINKING_TO_OUTGOING = 2;
106178           }
106179
106180           if ( EdgeEndStar$$1 ) { DirectedEdgeStar.__proto__ = EdgeEndStar$$1; }
106181           DirectedEdgeStar.prototype = Object.create( EdgeEndStar$$1 && EdgeEndStar$$1.prototype );
106182           DirectedEdgeStar.prototype.constructor = DirectedEdgeStar;
106183           DirectedEdgeStar.prototype.linkResultDirectedEdges = function linkResultDirectedEdges () {
106184             var this$1 = this;
106185
106186             this.getResultAreaEdges();
106187             var firstOut = null;
106188             var incoming = null;
106189             var state = this._SCANNING_FOR_INCOMING;
106190             for (var i = 0; i < this._resultAreaEdgeList.size(); i++) {
106191               var nextOut = this$1._resultAreaEdgeList.get(i);
106192               var nextIn = nextOut.getSym();
106193               if (!nextOut.getLabel().isArea()) { continue }
106194               if (firstOut === null && nextOut.isInResult()) { firstOut = nextOut; }
106195               switch (state) {
106196                 case this$1._SCANNING_FOR_INCOMING:
106197                   if (!nextIn.isInResult()) { continue }
106198                   incoming = nextIn;
106199                   state = this$1._LINKING_TO_OUTGOING;
106200                   break
106201                 case this$1._LINKING_TO_OUTGOING:
106202                   if (!nextOut.isInResult()) { continue }
106203                   incoming.setNext(nextOut);
106204                   state = this$1._SCANNING_FOR_INCOMING;
106205                   break
106206               }
106207             }
106208             if (state === this._LINKING_TO_OUTGOING) {
106209               if (firstOut === null) { throw new TopologyException('no outgoing dirEdge found', this.getCoordinate()) }
106210               Assert.isTrue(firstOut.isInResult(), 'unable to link last incoming dirEdge');
106211               incoming.setNext(firstOut);
106212             }
106213           };
106214           DirectedEdgeStar.prototype.insert = function insert (ee) {
106215             var de = ee;
106216             this.insertEdgeEnd(de, de);
106217           };
106218           DirectedEdgeStar.prototype.getRightmostEdge = function getRightmostEdge () {
106219             var edges = this.getEdges();
106220             var size = edges.size();
106221             if (size < 1) { return null }
106222             var de0 = edges.get(0);
106223             if (size === 1) { return de0 }
106224             var deLast = edges.get(size - 1);
106225             var quad0 = de0.getQuadrant();
106226             var quad1 = deLast.getQuadrant();
106227             if (Quadrant.isNorthern(quad0) && Quadrant.isNorthern(quad1)) { return de0; } else if (!Quadrant.isNorthern(quad0) && !Quadrant.isNorthern(quad1)) { return deLast; } else {
106228               // const nonHorizontalEdge = null
106229               if (de0.getDy() !== 0) { return de0; } else if (deLast.getDy() !== 0) { return deLast }
106230             }
106231             Assert.shouldNeverReachHere('found two horizontal edges incident on node');
106232             return null
106233           };
106234           DirectedEdgeStar.prototype.print = function print (out) {
106235             System.out.println('DirectedEdgeStar: ' + this.getCoordinate());
106236             for (var it = this.iterator(); it.hasNext();) {
106237               var de = it.next();
106238               out.print('out ');
106239               de.print(out);
106240               out.println();
106241               out.print('in ');
106242               de.getSym().print(out);
106243               out.println();
106244             }
106245           };
106246           DirectedEdgeStar.prototype.getResultAreaEdges = function getResultAreaEdges () {
106247             var this$1 = this;
106248
106249             if (this._resultAreaEdgeList !== null) { return this._resultAreaEdgeList }
106250             this._resultAreaEdgeList = new ArrayList();
106251             for (var it = this.iterator(); it.hasNext();) {
106252               var de = it.next();
106253               if (de.isInResult() || de.getSym().isInResult()) { this$1._resultAreaEdgeList.add(de); }
106254             }
106255             return this._resultAreaEdgeList
106256           };
106257           DirectedEdgeStar.prototype.updateLabelling = function updateLabelling (nodeLabel) {
106258             for (var it = this.iterator(); it.hasNext();) {
106259               var de = it.next();
106260               var label = de.getLabel();
106261               label.setAllLocationsIfNull(0, nodeLabel.getLocation(0));
106262               label.setAllLocationsIfNull(1, nodeLabel.getLocation(1));
106263             }
106264           };
106265           DirectedEdgeStar.prototype.linkAllDirectedEdges = function linkAllDirectedEdges () {
106266             var this$1 = this;
106267
106268             this.getEdges();
106269             var prevOut = null;
106270             var firstIn = null;
106271             for (var i = this._edgeList.size() - 1; i >= 0; i--) {
106272               var nextOut = this$1._edgeList.get(i);
106273               var nextIn = nextOut.getSym();
106274               if (firstIn === null) { firstIn = nextIn; }
106275               if (prevOut !== null) { nextIn.setNext(prevOut); }
106276               prevOut = nextOut;
106277             }
106278             firstIn.setNext(prevOut);
106279           };
106280           DirectedEdgeStar.prototype.computeDepths = function computeDepths () {
106281             var this$1 = this;
106282
106283             if (arguments.length === 1) {
106284               var de = arguments[0];
106285               var edgeIndex = this.findIndex(de);
106286               // const label = de.getLabel()
106287               var startDepth = de.getDepth(Position.LEFT);
106288               var targetLastDepth = de.getDepth(Position.RIGHT);
106289               var nextDepth = this.computeDepths(edgeIndex + 1, this._edgeList.size(), startDepth);
106290               var lastDepth = this.computeDepths(0, edgeIndex, nextDepth);
106291               if (lastDepth !== targetLastDepth) { throw new TopologyException('depth mismatch at ' + de.getCoordinate()) }
106292             } else if (arguments.length === 3) {
106293               var startIndex = arguments[0];
106294               var endIndex = arguments[1];
106295               var startDepth$1 = arguments[2];
106296               var currDepth = startDepth$1;
106297               for (var i = startIndex; i < endIndex; i++) {
106298                 var nextDe = this$1._edgeList.get(i);
106299                 // const label = nextDe.getLabel()
106300                 nextDe.setEdgeDepths(Position.RIGHT, currDepth);
106301                 currDepth = nextDe.getDepth(Position.LEFT);
106302               }
106303               return currDepth
106304             }
106305           };
106306           DirectedEdgeStar.prototype.mergeSymLabels = function mergeSymLabels () {
106307             for (var it = this.iterator(); it.hasNext();) {
106308               var de = it.next();
106309               var label = de.getLabel();
106310               label.merge(de.getSym().getLabel());
106311             }
106312           };
106313           DirectedEdgeStar.prototype.linkMinimalDirectedEdges = function linkMinimalDirectedEdges (er) {
106314             var this$1 = this;
106315
106316             var firstOut = null;
106317             var incoming = null;
106318             var state = this._SCANNING_FOR_INCOMING;
106319             for (var i = this._resultAreaEdgeList.size() - 1; i >= 0; i--) {
106320               var nextOut = this$1._resultAreaEdgeList.get(i);
106321               var nextIn = nextOut.getSym();
106322               if (firstOut === null && nextOut.getEdgeRing() === er) { firstOut = nextOut; }
106323               switch (state) {
106324                 case this$1._SCANNING_FOR_INCOMING:
106325                   if (nextIn.getEdgeRing() !== er) { continue }
106326                   incoming = nextIn;
106327                   state = this$1._LINKING_TO_OUTGOING;
106328                   break
106329                 case this$1._LINKING_TO_OUTGOING:
106330                   if (nextOut.getEdgeRing() !== er) { continue }
106331                   incoming.setNextMin(nextOut);
106332                   state = this$1._SCANNING_FOR_INCOMING;
106333                   break
106334               }
106335             }
106336             if (state === this._LINKING_TO_OUTGOING) {
106337               Assert.isTrue(firstOut !== null, 'found null for first outgoing dirEdge');
106338               Assert.isTrue(firstOut.getEdgeRing() === er, 'unable to link last incoming dirEdge');
106339               incoming.setNextMin(firstOut);
106340             }
106341           };
106342           DirectedEdgeStar.prototype.getOutgoingDegree = function getOutgoingDegree () {
106343             if (arguments.length === 0) {
106344               var degree = 0;
106345               for (var it = this.iterator(); it.hasNext();) {
106346                 var de = it.next();
106347                 if (de.isInResult()) { degree++; }
106348               }
106349               return degree
106350             } else if (arguments.length === 1) {
106351               var er = arguments[0];
106352               var degree$1 = 0;
106353               for (var it$1 = this.iterator(); it$1.hasNext();) {
106354                 var de$1 = it$1.next();
106355                 if (de$1.getEdgeRing() === er) { degree$1++; }
106356               }
106357               return degree$1
106358             }
106359           };
106360           DirectedEdgeStar.prototype.getLabel = function getLabel () {
106361             return this._label
106362           };
106363           DirectedEdgeStar.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
106364             var startLoc = Location.NONE;
106365             for (var it = this.iterator(); it.hasNext();) {
106366               var nextOut = it.next();
106367               var nextIn = nextOut.getSym();
106368               if (!nextOut.isLineEdge()) {
106369                 if (nextOut.isInResult()) {
106370                   startLoc = Location.INTERIOR;
106371                   break
106372                 }
106373                 if (nextIn.isInResult()) {
106374                   startLoc = Location.EXTERIOR;
106375                   break
106376                 }
106377               }
106378             }
106379             if (startLoc === Location.NONE) { return null }
106380             var currLoc = startLoc;
106381             for (var it$1 = this.iterator(); it$1.hasNext();) {
106382               var nextOut$1 = it$1.next();
106383               var nextIn$1 = nextOut$1.getSym();
106384               if (nextOut$1.isLineEdge()) {
106385                 nextOut$1.getEdge().setCovered(currLoc === Location.INTERIOR);
106386               } else {
106387                 if (nextOut$1.isInResult()) { currLoc = Location.EXTERIOR; }
106388                 if (nextIn$1.isInResult()) { currLoc = Location.INTERIOR; }
106389               }
106390             }
106391           };
106392           DirectedEdgeStar.prototype.computeLabelling = function computeLabelling (geom) {
106393             var this$1 = this;
106394
106395             EdgeEndStar$$1.prototype.computeLabelling.call(this, geom);
106396             this._label = new Label(Location.NONE);
106397             for (var it = this.iterator(); it.hasNext();) {
106398               var ee = it.next();
106399               var e = ee.getEdge();
106400               var eLabel = e.getLabel();
106401               for (var i = 0; i < 2; i++) {
106402                 var eLoc = eLabel.getLocation(i);
106403                 if (eLoc === Location.INTERIOR || eLoc === Location.BOUNDARY) { this$1._label.setLocation(i, Location.INTERIOR); }
106404               }
106405             }
106406           };
106407           DirectedEdgeStar.prototype.interfaces_ = function interfaces_ () {
106408             return []
106409           };
106410           DirectedEdgeStar.prototype.getClass = function getClass () {
106411             return DirectedEdgeStar
106412           };
106413
106414           return DirectedEdgeStar;
106415         }(EdgeEndStar));
106416
106417         var OverlayNodeFactory = (function (NodeFactory$$1) {
106418           function OverlayNodeFactory () {
106419             NodeFactory$$1.apply(this, arguments);
106420           }
106421
106422           if ( NodeFactory$$1 ) { OverlayNodeFactory.__proto__ = NodeFactory$$1; }
106423           OverlayNodeFactory.prototype = Object.create( NodeFactory$$1 && NodeFactory$$1.prototype );
106424           OverlayNodeFactory.prototype.constructor = OverlayNodeFactory;
106425
106426           OverlayNodeFactory.prototype.createNode = function createNode (coord) {
106427             return new Node$1(coord, new DirectedEdgeStar())
106428           };
106429           OverlayNodeFactory.prototype.interfaces_ = function interfaces_ () {
106430             return []
106431           };
106432           OverlayNodeFactory.prototype.getClass = function getClass () {
106433             return OverlayNodeFactory
106434           };
106435
106436           return OverlayNodeFactory;
106437         }(NodeFactory));
106438
106439         var OrientedCoordinateArray = function OrientedCoordinateArray () {
106440           this._pts = null;
106441           this._orientation = null;
106442           var pts = arguments[0];
106443           this._pts = pts;
106444           this._orientation = OrientedCoordinateArray.orientation(pts);
106445         };
106446         OrientedCoordinateArray.prototype.compareTo = function compareTo (o1) {
106447           var oca = o1;
106448           var comp = OrientedCoordinateArray.compareOriented(this._pts, this._orientation, oca._pts, oca._orientation);
106449           return comp
106450         };
106451         OrientedCoordinateArray.prototype.interfaces_ = function interfaces_ () {
106452           return [Comparable]
106453         };
106454         OrientedCoordinateArray.prototype.getClass = function getClass () {
106455           return OrientedCoordinateArray
106456         };
106457         OrientedCoordinateArray.orientation = function orientation (pts) {
106458           return CoordinateArrays.increasingDirection(pts) === 1
106459         };
106460         OrientedCoordinateArray.compareOriented = function compareOriented (pts1, orientation1, pts2, orientation2) {
106461           var dir1 = orientation1 ? 1 : -1;
106462           var dir2 = orientation2 ? 1 : -1;
106463           var limit1 = orientation1 ? pts1.length : -1;
106464           var limit2 = orientation2 ? pts2.length : -1;
106465           var i1 = orientation1 ? 0 : pts1.length - 1;
106466           var i2 = orientation2 ? 0 : pts2.length - 1;
106467           // const comp = 0
106468           while (true) {
106469             var compPt = pts1[i1].compareTo(pts2[i2]);
106470             if (compPt !== 0) { return compPt }
106471             i1 += dir1;
106472             i2 += dir2;
106473             var done1 = i1 === limit1;
106474             var done2 = i2 === limit2;
106475             if (done1 && !done2) { return -1 }
106476             if (!done1 && done2) { return 1 }
106477             if (done1 && done2) { return 0 }
106478           }
106479         };
106480
106481         var EdgeList = function EdgeList () {
106482           this._edges = new ArrayList();
106483           this._ocaMap = new TreeMap();
106484         };
106485         EdgeList.prototype.print = function print (out) {
106486             var this$1 = this;
106487
106488           out.print('MULTILINESTRING ( ');
106489           for (var j = 0; j < this._edges.size(); j++) {
106490             var e = this$1._edges.get(j);
106491             if (j > 0) { out.print(','); }
106492             out.print('(');
106493             var pts = e.getCoordinates();
106494             for (var i = 0; i < pts.length; i++) {
106495               if (i > 0) { out.print(','); }
106496               out.print(pts[i].x + ' ' + pts[i].y);
106497             }
106498             out.println(')');
106499           }
106500           out.print(')  ');
106501         };
106502         EdgeList.prototype.addAll = function addAll (edgeColl) {
106503             var this$1 = this;
106504
106505           for (var i = edgeColl.iterator(); i.hasNext();) {
106506             this$1.add(i.next());
106507           }
106508         };
106509         EdgeList.prototype.findEdgeIndex = function findEdgeIndex (e) {
106510             var this$1 = this;
106511
106512           for (var i = 0; i < this._edges.size(); i++) {
106513             if (this$1._edges.get(i).equals(e)) { return i }
106514           }
106515           return -1
106516         };
106517         EdgeList.prototype.iterator = function iterator () {
106518           return this._edges.iterator()
106519         };
106520         EdgeList.prototype.getEdges = function getEdges () {
106521           return this._edges
106522         };
106523         EdgeList.prototype.get = function get (i) {
106524           return this._edges.get(i)
106525         };
106526         EdgeList.prototype.findEqualEdge = function findEqualEdge (e) {
106527           var oca = new OrientedCoordinateArray(e.getCoordinates());
106528           var matchEdge = this._ocaMap.get(oca);
106529           return matchEdge
106530         };
106531         EdgeList.prototype.add = function add (e) {
106532           this._edges.add(e);
106533           var oca = new OrientedCoordinateArray(e.getCoordinates());
106534           this._ocaMap.put(oca, e);
106535         };
106536         EdgeList.prototype.interfaces_ = function interfaces_ () {
106537           return []
106538         };
106539         EdgeList.prototype.getClass = function getClass () {
106540           return EdgeList
106541         };
106542
106543         var SegmentIntersector = function SegmentIntersector () {};
106544
106545         SegmentIntersector.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {};
106546         SegmentIntersector.prototype.isDone = function isDone () {};
106547         SegmentIntersector.prototype.interfaces_ = function interfaces_ () {
106548           return []
106549         };
106550         SegmentIntersector.prototype.getClass = function getClass () {
106551           return SegmentIntersector
106552         };
106553
106554         var IntersectionAdder = function IntersectionAdder () {
106555           this._hasIntersection = false;
106556           this._hasProper = false;
106557           this._hasProperInterior = false;
106558           this._hasInterior = false;
106559           this._properIntersectionPoint = null;
106560           this._li = null;
106561           this._isSelfIntersection = null;
106562           this.numIntersections = 0;
106563           this.numInteriorIntersections = 0;
106564           this.numProperIntersections = 0;
106565           this.numTests = 0;
106566           var li = arguments[0];
106567           this._li = li;
106568         };
106569         IntersectionAdder.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
106570           if (e0 === e1) {
106571             if (this._li.getIntersectionNum() === 1) {
106572               if (IntersectionAdder.isAdjacentSegments(segIndex0, segIndex1)) { return true }
106573               if (e0.isClosed()) {
106574                 var maxSegIndex = e0.size() - 1;
106575                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
106576                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
106577                   return true
106578                 }
106579               }
106580             }
106581           }
106582           return false
106583         };
106584         IntersectionAdder.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
106585           return this._properIntersectionPoint
106586         };
106587         IntersectionAdder.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
106588           return this._hasProperInterior
106589         };
106590         IntersectionAdder.prototype.getLineIntersector = function getLineIntersector () {
106591           return this._li
106592         };
106593         IntersectionAdder.prototype.hasProperIntersection = function hasProperIntersection () {
106594           return this._hasProper
106595         };
106596         IntersectionAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
106597           if (e0 === e1 && segIndex0 === segIndex1) { return null }
106598           this.numTests++;
106599           var p00 = e0.getCoordinates()[segIndex0];
106600           var p01 = e0.getCoordinates()[segIndex0 + 1];
106601           var p10 = e1.getCoordinates()[segIndex1];
106602           var p11 = e1.getCoordinates()[segIndex1 + 1];
106603           this._li.computeIntersection(p00, p01, p10, p11);
106604           if (this._li.hasIntersection()) {
106605             this.numIntersections++;
106606             if (this._li.isInteriorIntersection()) {
106607               this.numInteriorIntersections++;
106608               this._hasInterior = true;
106609             }
106610             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
106611               this._hasIntersection = true;
106612               e0.addIntersections(this._li, segIndex0, 0);
106613               e1.addIntersections(this._li, segIndex1, 1);
106614               if (this._li.isProper()) {
106615                 this.numProperIntersections++;
106616                 this._hasProper = true;
106617                 this._hasProperInterior = true;
106618               }
106619             }
106620           }
106621         };
106622         IntersectionAdder.prototype.hasIntersection = function hasIntersection () {
106623           return this._hasIntersection
106624         };
106625         IntersectionAdder.prototype.isDone = function isDone () {
106626           return false
106627         };
106628         IntersectionAdder.prototype.hasInteriorIntersection = function hasInteriorIntersection () {
106629           return this._hasInterior
106630         };
106631         IntersectionAdder.prototype.interfaces_ = function interfaces_ () {
106632           return [SegmentIntersector]
106633         };
106634         IntersectionAdder.prototype.getClass = function getClass () {
106635           return IntersectionAdder
106636         };
106637         IntersectionAdder.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
106638           return Math.abs(i1 - i2) === 1
106639         };
106640
106641         var EdgeIntersection = function EdgeIntersection () {
106642           this.coord = null;
106643           this.segmentIndex = null;
106644           this.dist = null;
106645           var coord = arguments[0];
106646           var segmentIndex = arguments[1];
106647           var dist = arguments[2];
106648           this.coord = new Coordinate(coord);
106649           this.segmentIndex = segmentIndex;
106650           this.dist = dist;
106651         };
106652         EdgeIntersection.prototype.getSegmentIndex = function getSegmentIndex () {
106653           return this.segmentIndex
106654         };
106655         EdgeIntersection.prototype.getCoordinate = function getCoordinate () {
106656           return this.coord
106657         };
106658         EdgeIntersection.prototype.print = function print (out) {
106659           out.print(this.coord);
106660           out.print(' seg # = ' + this.segmentIndex);
106661           out.println(' dist = ' + this.dist);
106662         };
106663         EdgeIntersection.prototype.compareTo = function compareTo (obj) {
106664           var other = obj;
106665           return this.compare(other.segmentIndex, other.dist)
106666         };
106667         EdgeIntersection.prototype.isEndPoint = function isEndPoint (maxSegmentIndex) {
106668           if (this.segmentIndex === 0 && this.dist === 0.0) { return true }
106669           if (this.segmentIndex === maxSegmentIndex) { return true }
106670           return false
106671         };
106672         EdgeIntersection.prototype.toString = function toString () {
106673           return this.coord + ' seg # = ' + this.segmentIndex + ' dist = ' + this.dist
106674         };
106675         EdgeIntersection.prototype.getDistance = function getDistance () {
106676           return this.dist
106677         };
106678         EdgeIntersection.prototype.compare = function compare (segmentIndex, dist) {
106679           if (this.segmentIndex < segmentIndex) { return -1 }
106680           if (this.segmentIndex > segmentIndex) { return 1 }
106681           if (this.dist < dist) { return -1 }
106682           if (this.dist > dist) { return 1 }
106683           return 0
106684         };
106685         EdgeIntersection.prototype.interfaces_ = function interfaces_ () {
106686           return [Comparable]
106687         };
106688         EdgeIntersection.prototype.getClass = function getClass () {
106689           return EdgeIntersection
106690         };
106691
106692         var EdgeIntersectionList = function EdgeIntersectionList () {
106693           this._nodeMap = new TreeMap();
106694           this.edge = null;
106695           var edge = arguments[0];
106696           this.edge = edge;
106697         };
106698         EdgeIntersectionList.prototype.print = function print (out) {
106699           out.println('Intersections:');
106700           for (var it = this.iterator(); it.hasNext();) {
106701             var ei = it.next();
106702             ei.print(out);
106703           }
106704         };
106705         EdgeIntersectionList.prototype.iterator = function iterator () {
106706           return this._nodeMap.values().iterator()
106707         };
106708         EdgeIntersectionList.prototype.addSplitEdges = function addSplitEdges (edgeList) {
106709             var this$1 = this;
106710
106711           this.addEndpoints();
106712           var it = this.iterator();
106713           var eiPrev = it.next();
106714           while (it.hasNext()) {
106715             var ei = it.next();
106716             var newEdge = this$1.createSplitEdge(eiPrev, ei);
106717             edgeList.add(newEdge);
106718             eiPrev = ei;
106719           }
106720         };
106721         EdgeIntersectionList.prototype.addEndpoints = function addEndpoints () {
106722           var maxSegIndex = this.edge.pts.length - 1;
106723           this.add(this.edge.pts[0], 0, 0.0);
106724           this.add(this.edge.pts[maxSegIndex], maxSegIndex, 0.0);
106725         };
106726         EdgeIntersectionList.prototype.createSplitEdge = function createSplitEdge (ei0, ei1) {
106727             var this$1 = this;
106728
106729           var npts = ei1.segmentIndex - ei0.segmentIndex + 2;
106730           var lastSegStartPt = this.edge.pts[ei1.segmentIndex];
106731           var useIntPt1 = ei1.dist > 0.0 || !ei1.coord.equals2D(lastSegStartPt);
106732           if (!useIntPt1) {
106733             npts--;
106734           }
106735           var pts = new Array(npts).fill(null);
106736           var ipt = 0;
106737           pts[ipt++] = new Coordinate(ei0.coord);
106738           for (var i = ei0.segmentIndex + 1; i <= ei1.segmentIndex; i++) {
106739             pts[ipt++] = this$1.edge.pts[i];
106740           }
106741           if (useIntPt1) { pts[ipt] = ei1.coord; }
106742           return new Edge(pts, new Label(this.edge._label))
106743         };
106744         EdgeIntersectionList.prototype.add = function add (intPt, segmentIndex, dist) {
106745           var eiNew = new EdgeIntersection(intPt, segmentIndex, dist);
106746           var ei = this._nodeMap.get(eiNew);
106747           if (ei !== null) {
106748             return ei
106749           }
106750           this._nodeMap.put(eiNew, eiNew);
106751           return eiNew
106752         };
106753         EdgeIntersectionList.prototype.isIntersection = function isIntersection (pt) {
106754           for (var it = this.iterator(); it.hasNext();) {
106755             var ei = it.next();
106756             if (ei.coord.equals(pt)) { return true }
106757           }
106758           return false
106759         };
106760         EdgeIntersectionList.prototype.interfaces_ = function interfaces_ () {
106761           return []
106762         };
106763         EdgeIntersectionList.prototype.getClass = function getClass () {
106764           return EdgeIntersectionList
106765         };
106766
106767         var MonotoneChainIndexer = function MonotoneChainIndexer () {};
106768
106769         MonotoneChainIndexer.prototype.getChainStartIndices = function getChainStartIndices (pts) {
106770             var this$1 = this;
106771
106772           var start = 0;
106773           var startIndexList = new ArrayList();
106774           startIndexList.add(new Integer(start));
106775           do {
106776             var last = this$1.findChainEnd(pts, start);
106777             startIndexList.add(new Integer(last));
106778             start = last;
106779           } while (start < pts.length - 1)
106780           var startIndex = MonotoneChainIndexer.toIntArray(startIndexList);
106781           return startIndex
106782         };
106783         MonotoneChainIndexer.prototype.findChainEnd = function findChainEnd (pts, start) {
106784           var chainQuad = Quadrant.quadrant(pts[start], pts[start + 1]);
106785           var last = start + 1;
106786           while (last < pts.length) {
106787             var quad = Quadrant.quadrant(pts[last - 1], pts[last]);
106788             if (quad !== chainQuad) { break }
106789             last++;
106790           }
106791           return last - 1
106792         };
106793         MonotoneChainIndexer.prototype.interfaces_ = function interfaces_ () {
106794           return []
106795         };
106796         MonotoneChainIndexer.prototype.getClass = function getClass () {
106797           return MonotoneChainIndexer
106798         };
106799         MonotoneChainIndexer.toIntArray = function toIntArray (list) {
106800           var array = new Array(list.size()).fill(null);
106801           for (var i = 0; i < array.length; i++) {
106802             array[i] = list.get(i).intValue();
106803           }
106804           return array
106805         };
106806
106807         var MonotoneChainEdge = function MonotoneChainEdge () {
106808           this.e = null;
106809           this.pts = null;
106810           this.startIndex = null;
106811           this.env1 = new Envelope();
106812           this.env2 = new Envelope();
106813           var e = arguments[0];
106814           this.e = e;
106815           this.pts = e.getCoordinates();
106816           var mcb = new MonotoneChainIndexer();
106817           this.startIndex = mcb.getChainStartIndices(this.pts);
106818         };
106819         MonotoneChainEdge.prototype.getCoordinates = function getCoordinates () {
106820           return this.pts
106821         };
106822         MonotoneChainEdge.prototype.getMaxX = function getMaxX (chainIndex) {
106823           var x1 = this.pts[this.startIndex[chainIndex]].x;
106824           var x2 = this.pts[this.startIndex[chainIndex + 1]].x;
106825           return x1 > x2 ? x1 : x2
106826         };
106827         MonotoneChainEdge.prototype.getMinX = function getMinX (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.computeIntersectsForChain = function computeIntersectsForChain () {
106833           if (arguments.length === 4) {
106834             var chainIndex0 = arguments[0];
106835             var mce = arguments[1];
106836             var chainIndex1 = arguments[2];
106837             var si = arguments[3];
106838             this.computeIntersectsForChain(this.startIndex[chainIndex0], this.startIndex[chainIndex0 + 1], mce, mce.startIndex[chainIndex1], mce.startIndex[chainIndex1 + 1], si);
106839           } else if (arguments.length === 6) {
106840             var start0 = arguments[0];
106841             var end0 = arguments[1];
106842             var mce$1 = arguments[2];
106843             var start1 = arguments[3];
106844             var end1 = arguments[4];
106845             var ei = arguments[5];
106846             var p00 = this.pts[start0];
106847             var p01 = this.pts[end0];
106848             var p10 = mce$1.pts[start1];
106849             var p11 = mce$1.pts[end1];
106850             if (end0 - start0 === 1 && end1 - start1 === 1) {
106851               ei.addIntersections(this.e, start0, mce$1.e, start1);
106852               return null
106853             }
106854             this.env1.init(p00, p01);
106855             this.env2.init(p10, p11);
106856             if (!this.env1.intersects(this.env2)) { return null }
106857             var mid0 = Math.trunc((start0 + end0) / 2);
106858             var mid1 = Math.trunc((start1 + end1) / 2);
106859             if (start0 < mid0) {
106860               if (start1 < mid1) { this.computeIntersectsForChain(start0, mid0, mce$1, start1, mid1, ei); }
106861               if (mid1 < end1) { this.computeIntersectsForChain(start0, mid0, mce$1, mid1, end1, ei); }
106862             }
106863             if (mid0 < end0) {
106864               if (start1 < mid1) { this.computeIntersectsForChain(mid0, end0, mce$1, start1, mid1, ei); }
106865               if (mid1 < end1) { this.computeIntersectsForChain(mid0, end0, mce$1, mid1, end1, ei); }
106866             }
106867           }
106868         };
106869         MonotoneChainEdge.prototype.getStartIndexes = function getStartIndexes () {
106870           return this.startIndex
106871         };
106872         MonotoneChainEdge.prototype.computeIntersects = function computeIntersects (mce, si) {
106873             var this$1 = this;
106874
106875           for (var i = 0; i < this.startIndex.length - 1; i++) {
106876             for (var j = 0; j < mce.startIndex.length - 1; j++) {
106877               this$1.computeIntersectsForChain(i, mce, j, si);
106878             }
106879           }
106880         };
106881         MonotoneChainEdge.prototype.interfaces_ = function interfaces_ () {
106882           return []
106883         };
106884         MonotoneChainEdge.prototype.getClass = function getClass () {
106885           return MonotoneChainEdge
106886         };
106887
106888         var Depth = function Depth () {
106889           var this$1 = this;
106890
106891           this._depth = Array(2).fill().map(function () { return Array(3); });
106892           for (var i = 0; i < 2; i++) {
106893             for (var j = 0; j < 3; j++) {
106894               this$1._depth[i][j] = Depth.NULL_VALUE;
106895             }
106896           }
106897         };
106898
106899         var staticAccessors$31 = { NULL_VALUE: { configurable: true } };
106900         Depth.prototype.getDepth = function getDepth (geomIndex, posIndex) {
106901           return this._depth[geomIndex][posIndex]
106902         };
106903         Depth.prototype.setDepth = function setDepth (geomIndex, posIndex, depthValue) {
106904           this._depth[geomIndex][posIndex] = depthValue;
106905         };
106906         Depth.prototype.isNull = function isNull () {
106907             var this$1 = this;
106908
106909           if (arguments.length === 0) {
106910             for (var i = 0; i < 2; i++) {
106911               for (var j = 0; j < 3; j++) {
106912                 if (this$1._depth[i][j] !== Depth.NULL_VALUE) { return false }
106913               }
106914             }
106915             return true
106916           } else if (arguments.length === 1) {
106917             var geomIndex = arguments[0];
106918             return this._depth[geomIndex][1] === Depth.NULL_VALUE
106919           } else if (arguments.length === 2) {
106920             var geomIndex$1 = arguments[0];
106921             var posIndex = arguments[1];
106922             return this._depth[geomIndex$1][posIndex] === Depth.NULL_VALUE
106923           }
106924         };
106925         Depth.prototype.normalize = function normalize () {
106926             var this$1 = this;
106927
106928           for (var i = 0; i < 2; i++) {
106929             if (!this$1.isNull(i)) {
106930               var minDepth = this$1._depth[i][1];
106931               if (this$1._depth[i][2] < minDepth) { minDepth = this$1._depth[i][2]; }
106932               if (minDepth < 0) { minDepth = 0; }
106933               for (var j = 1; j < 3; j++) {
106934                 var newValue = 0;
106935                 if (this$1._depth[i][j] > minDepth) { newValue = 1; }
106936                 this$1._depth[i][j] = newValue;
106937               }
106938             }
106939           }
106940         };
106941         Depth.prototype.getDelta = function getDelta (geomIndex) {
106942           return this._depth[geomIndex][Position.RIGHT] - this._depth[geomIndex][Position.LEFT]
106943         };
106944         Depth.prototype.getLocation = function getLocation (geomIndex, posIndex) {
106945           if (this._depth[geomIndex][posIndex] <= 0) { return Location.EXTERIOR }
106946           return Location.INTERIOR
106947         };
106948         Depth.prototype.toString = function toString () {
106949           return 'A: ' + this._depth[0][1] + ',' + this._depth[0][2] + ' B: ' + this._depth[1][1] + ',' + this._depth[1][2]
106950         };
106951         Depth.prototype.add = function add () {
106952             var this$1 = this;
106953
106954           if (arguments.length === 1) {
106955             var lbl = arguments[0];
106956             for (var i = 0; i < 2; i++) {
106957               for (var j = 1; j < 3; j++) {
106958                 var loc = lbl.getLocation(i, j);
106959                 if (loc === Location.EXTERIOR || loc === Location.INTERIOR) {
106960                   if (this$1.isNull(i, j)) {
106961                     this$1._depth[i][j] = Depth.depthAtLocation(loc);
106962                   } else { this$1._depth[i][j] += Depth.depthAtLocation(loc); }
106963                 }
106964               }
106965             }
106966           } else if (arguments.length === 3) {
106967             var geomIndex = arguments[0];
106968             var posIndex = arguments[1];
106969             var location = arguments[2];
106970             if (location === Location.INTERIOR) { this._depth[geomIndex][posIndex]++; }
106971           }
106972         };
106973         Depth.prototype.interfaces_ = function interfaces_ () {
106974           return []
106975         };
106976         Depth.prototype.getClass = function getClass () {
106977           return Depth
106978         };
106979         Depth.depthAtLocation = function depthAtLocation (location) {
106980           if (location === Location.EXTERIOR) { return 0 }
106981           if (location === Location.INTERIOR) { return 1 }
106982           return Depth.NULL_VALUE
106983         };
106984         staticAccessors$31.NULL_VALUE.get = function () { return -1 };
106985
106986         Object.defineProperties( Depth, staticAccessors$31 );
106987
106988         var Edge = (function (GraphComponent$$1) {
106989           function Edge () {
106990             GraphComponent$$1.call(this);
106991             this.pts = null;
106992             this._env = null;
106993             this.eiList = new EdgeIntersectionList(this);
106994             this._name = null;
106995             this._mce = null;
106996             this._isIsolated = true;
106997             this._depth = new Depth();
106998             this._depthDelta = 0;
106999             if (arguments.length === 1) {
107000               var pts = arguments[0];
107001               Edge.call(this, pts, null);
107002             } else if (arguments.length === 2) {
107003               var pts$1 = arguments[0];
107004               var label = arguments[1];
107005               this.pts = pts$1;
107006               this._label = label;
107007             }
107008           }
107009
107010           if ( GraphComponent$$1 ) { Edge.__proto__ = GraphComponent$$1; }
107011           Edge.prototype = Object.create( GraphComponent$$1 && GraphComponent$$1.prototype );
107012           Edge.prototype.constructor = Edge;
107013           Edge.prototype.getDepth = function getDepth () {
107014             return this._depth
107015           };
107016           Edge.prototype.getCollapsedEdge = function getCollapsedEdge () {
107017             var newPts = new Array(2).fill(null);
107018             newPts[0] = this.pts[0];
107019             newPts[1] = this.pts[1];
107020             var newe = new Edge(newPts, Label.toLineLabel(this._label));
107021             return newe
107022           };
107023           Edge.prototype.isIsolated = function isIsolated () {
107024             return this._isIsolated
107025           };
107026           Edge.prototype.getCoordinates = function getCoordinates () {
107027             return this.pts
107028           };
107029           Edge.prototype.setIsolated = function setIsolated (isIsolated) {
107030             this._isIsolated = isIsolated;
107031           };
107032           Edge.prototype.setName = function setName (name) {
107033             this._name = name;
107034           };
107035           Edge.prototype.equals = function equals (o) {
107036             var this$1 = this;
107037
107038             if (!(o instanceof Edge)) { return false }
107039             var e = o;
107040             if (this.pts.length !== e.pts.length) { return false }
107041             var isEqualForward = true;
107042             var isEqualReverse = true;
107043             var iRev = this.pts.length;
107044             for (var i = 0; i < this.pts.length; i++) {
107045               if (!this$1.pts[i].equals2D(e.pts[i])) {
107046                 isEqualForward = false;
107047               }
107048               if (!this$1.pts[i].equals2D(e.pts[--iRev])) {
107049                 isEqualReverse = false;
107050               }
107051               if (!isEqualForward && !isEqualReverse) { return false }
107052             }
107053             return true
107054           };
107055           Edge.prototype.getCoordinate = function getCoordinate () {
107056             if (arguments.length === 0) {
107057               if (this.pts.length > 0) { return this.pts[0] }
107058               return null
107059             } else if (arguments.length === 1) {
107060               var i = arguments[0];
107061               return this.pts[i]
107062             }
107063           };
107064           Edge.prototype.print = function print (out) {
107065             var this$1 = this;
107066
107067             out.print('edge ' + this._name + ': ');
107068             out.print('LINESTRING (');
107069             for (var i = 0; i < this.pts.length; i++) {
107070               if (i > 0) { out.print(','); }
107071               out.print(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107072             }
107073             out.print(')  ' + this._label + ' ' + this._depthDelta);
107074           };
107075           Edge.prototype.computeIM = function computeIM (im) {
107076             Edge.updateIM(this._label, im);
107077           };
107078           Edge.prototype.isCollapsed = function isCollapsed () {
107079             if (!this._label.isArea()) { return false }
107080             if (this.pts.length !== 3) { return false }
107081             if (this.pts[0].equals(this.pts[2])) { return true }
107082             return false
107083           };
107084           Edge.prototype.isClosed = function isClosed () {
107085             return this.pts[0].equals(this.pts[this.pts.length - 1])
107086           };
107087           Edge.prototype.getMaximumSegmentIndex = function getMaximumSegmentIndex () {
107088             return this.pts.length - 1
107089           };
107090           Edge.prototype.getDepthDelta = function getDepthDelta () {
107091             return this._depthDelta
107092           };
107093           Edge.prototype.getNumPoints = function getNumPoints () {
107094             return this.pts.length
107095           };
107096           Edge.prototype.printReverse = function printReverse (out) {
107097             var this$1 = this;
107098
107099             out.print('edge ' + this._name + ': ');
107100             for (var i = this.pts.length - 1; i >= 0; i--) {
107101               out.print(this$1.pts[i] + ' ');
107102             }
107103             out.println('');
107104           };
107105           Edge.prototype.getMonotoneChainEdge = function getMonotoneChainEdge () {
107106             if (this._mce === null) { this._mce = new MonotoneChainEdge(this); }
107107             return this._mce
107108           };
107109           Edge.prototype.getEnvelope = function getEnvelope () {
107110             var this$1 = this;
107111
107112             if (this._env === null) {
107113               this._env = new Envelope();
107114               for (var i = 0; i < this.pts.length; i++) {
107115                 this$1._env.expandToInclude(this$1.pts[i]);
107116               }
107117             }
107118             return this._env
107119           };
107120           Edge.prototype.addIntersection = function addIntersection (li, segmentIndex, geomIndex, intIndex) {
107121             var intPt = new Coordinate(li.getIntersection(intIndex));
107122             var normalizedSegmentIndex = segmentIndex;
107123             var dist = li.getEdgeDistance(geomIndex, intIndex);
107124             var nextSegIndex = normalizedSegmentIndex + 1;
107125             if (nextSegIndex < this.pts.length) {
107126               var nextPt = this.pts[nextSegIndex];
107127               if (intPt.equals2D(nextPt)) {
107128                 normalizedSegmentIndex = nextSegIndex;
107129                 dist = 0.0;
107130               }
107131             }
107132             this.eiList.add(intPt, normalizedSegmentIndex, dist);
107133           };
107134           Edge.prototype.toString = function toString () {
107135             var this$1 = this;
107136
107137             var buf = new StringBuffer();
107138             buf.append('edge ' + this._name + ': ');
107139             buf.append('LINESTRING (');
107140             for (var i = 0; i < this.pts.length; i++) {
107141               if (i > 0) { buf.append(','); }
107142               buf.append(this$1.pts[i].x + ' ' + this$1.pts[i].y);
107143             }
107144             buf.append(')  ' + this._label + ' ' + this._depthDelta);
107145             return buf.toString()
107146           };
107147           Edge.prototype.isPointwiseEqual = function isPointwiseEqual (e) {
107148             var this$1 = this;
107149
107150             if (this.pts.length !== e.pts.length) { return false }
107151             for (var i = 0; i < this.pts.length; i++) {
107152               if (!this$1.pts[i].equals2D(e.pts[i])) {
107153                 return false
107154               }
107155             }
107156             return true
107157           };
107158           Edge.prototype.setDepthDelta = function setDepthDelta (depthDelta) {
107159             this._depthDelta = depthDelta;
107160           };
107161           Edge.prototype.getEdgeIntersectionList = function getEdgeIntersectionList () {
107162             return this.eiList
107163           };
107164           Edge.prototype.addIntersections = function addIntersections (li, segmentIndex, geomIndex) {
107165             var this$1 = this;
107166
107167             for (var i = 0; i < li.getIntersectionNum(); i++) {
107168               this$1.addIntersection(li, segmentIndex, geomIndex, i);
107169             }
107170           };
107171           Edge.prototype.interfaces_ = function interfaces_ () {
107172             return []
107173           };
107174           Edge.prototype.getClass = function getClass () {
107175             return Edge
107176           };
107177           Edge.updateIM = function updateIM () {
107178             if (arguments.length === 2) {
107179               var label = arguments[0];
107180               var im = arguments[1];
107181               im.setAtLeastIfValid(label.getLocation(0, Position.ON), label.getLocation(1, Position.ON), 1);
107182               if (label.isArea()) {
107183                 im.setAtLeastIfValid(label.getLocation(0, Position.LEFT), label.getLocation(1, Position.LEFT), 2);
107184                 im.setAtLeastIfValid(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), 2);
107185               }
107186             } else { return GraphComponent$$1.prototype.updateIM.apply(this, arguments) }
107187           };
107188
107189           return Edge;
107190         }(GraphComponent));
107191
107192         var BufferBuilder = function BufferBuilder (bufParams) {
107193           this._workingPrecisionModel = null;
107194           this._workingNoder = null;
107195           this._geomFact = null;
107196           this._graph = null;
107197           this._edgeList = new EdgeList();
107198           this._bufParams = bufParams || null;
107199         };
107200         BufferBuilder.prototype.setWorkingPrecisionModel = function setWorkingPrecisionModel (pm) {
107201           this._workingPrecisionModel = pm;
107202         };
107203         BufferBuilder.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
107204           var existingEdge = this._edgeList.findEqualEdge(e);
107205           if (existingEdge !== null) {
107206             var existingLabel = existingEdge.getLabel();
107207             var labelToMerge = e.getLabel();
107208             if (!existingEdge.isPointwiseEqual(e)) {
107209               labelToMerge = new Label(e.getLabel());
107210               labelToMerge.flip();
107211             }
107212             existingLabel.merge(labelToMerge);
107213             var mergeDelta = BufferBuilder.depthDelta(labelToMerge);
107214             var existingDelta = existingEdge.getDepthDelta();
107215             var newDelta = existingDelta + mergeDelta;
107216             existingEdge.setDepthDelta(newDelta);
107217           } else {
107218             this._edgeList.add(e);
107219             e.setDepthDelta(BufferBuilder.depthDelta(e.getLabel()));
107220           }
107221         };
107222         BufferBuilder.prototype.buildSubgraphs = function buildSubgraphs (subgraphList, polyBuilder) {
107223           var processedGraphs = new ArrayList();
107224           for (var i = subgraphList.iterator(); i.hasNext();) {
107225             var subgraph = i.next();
107226             var p = subgraph.getRightmostCoordinate();
107227             var locater = new SubgraphDepthLocater(processedGraphs);
107228             var outsideDepth = locater.getDepth(p);
107229             subgraph.computeDepth(outsideDepth);
107230             subgraph.findResultEdges();
107231             processedGraphs.add(subgraph);
107232             polyBuilder.add(subgraph.getDirectedEdges(), subgraph.getNodes());
107233           }
107234         };
107235         BufferBuilder.prototype.createSubgraphs = function createSubgraphs (graph) {
107236           var subgraphList = new ArrayList();
107237           for (var i = graph.getNodes().iterator(); i.hasNext();) {
107238             var node = i.next();
107239             if (!node.isVisited()) {
107240               var subgraph = new BufferSubgraph();
107241               subgraph.create(node);
107242               subgraphList.add(subgraph);
107243             }
107244           }
107245           Collections.sort(subgraphList, Collections.reverseOrder());
107246           return subgraphList
107247         };
107248         BufferBuilder.prototype.createEmptyResultGeometry = function createEmptyResultGeometry () {
107249           var emptyGeom = this._geomFact.createPolygon();
107250           return emptyGeom
107251         };
107252         BufferBuilder.prototype.getNoder = function getNoder (precisionModel) {
107253           if (this._workingNoder !== null) { return this._workingNoder }
107254           var noder = new MCIndexNoder();
107255           var li = new RobustLineIntersector();
107256           li.setPrecisionModel(precisionModel);
107257           noder.setSegmentIntersector(new IntersectionAdder(li));
107258           return noder
107259         };
107260         BufferBuilder.prototype.buffer = function buffer (g, distance) {
107261           var precisionModel = this._workingPrecisionModel;
107262           if (precisionModel === null) { precisionModel = g.getPrecisionModel(); }
107263           this._geomFact = g.getFactory();
107264           var curveBuilder = new OffsetCurveBuilder(precisionModel, this._bufParams);
107265           var curveSetBuilder = new OffsetCurveSetBuilder(g, distance, curveBuilder);
107266           var bufferSegStrList = curveSetBuilder.getCurves();
107267           if (bufferSegStrList.size() <= 0) {
107268             return this.createEmptyResultGeometry()
107269           }
107270           this.computeNodedEdges(bufferSegStrList, precisionModel);
107271           this._graph = new PlanarGraph(new OverlayNodeFactory());
107272           this._graph.addEdges(this._edgeList.getEdges());
107273           var subgraphList = this.createSubgraphs(this._graph);
107274           var polyBuilder = new PolygonBuilder(this._geomFact);
107275           this.buildSubgraphs(subgraphList, polyBuilder);
107276           var resultPolyList = polyBuilder.getPolygons();
107277           if (resultPolyList.size() <= 0) {
107278             return this.createEmptyResultGeometry()
107279           }
107280           var resultGeom = this._geomFact.buildGeometry(resultPolyList);
107281           return resultGeom
107282         };
107283         BufferBuilder.prototype.computeNodedEdges = function computeNodedEdges (bufferSegStrList, precisionModel) {
107284             var this$1 = this;
107285
107286           var noder = this.getNoder(precisionModel);
107287           noder.computeNodes(bufferSegStrList);
107288           var nodedSegStrings = noder.getNodedSubstrings();
107289           for (var i = nodedSegStrings.iterator(); i.hasNext();) {
107290             var segStr = i.next();
107291             var pts = segStr.getCoordinates();
107292             if (pts.length === 2 && pts[0].equals2D(pts[1])) { continue }
107293             var oldLabel = segStr.getData();
107294             var edge = new Edge(segStr.getCoordinates(), new Label(oldLabel));
107295             this$1.insertUniqueEdge(edge);
107296           }
107297         };
107298         BufferBuilder.prototype.setNoder = function setNoder (noder) {
107299           this._workingNoder = noder;
107300         };
107301         BufferBuilder.prototype.interfaces_ = function interfaces_ () {
107302           return []
107303         };
107304         BufferBuilder.prototype.getClass = function getClass () {
107305           return BufferBuilder
107306         };
107307         BufferBuilder.depthDelta = function depthDelta (label) {
107308           var lLoc = label.getLocation(0, Position.LEFT);
107309           var rLoc = label.getLocation(0, Position.RIGHT);
107310           if (lLoc === Location.INTERIOR && rLoc === Location.EXTERIOR) { return 1; } else if (lLoc === Location.EXTERIOR && rLoc === Location.INTERIOR) { return -1 }
107311           return 0
107312         };
107313         BufferBuilder.convertSegStrings = function convertSegStrings (it) {
107314           var fact = new GeometryFactory();
107315           var lines = new ArrayList();
107316           while (it.hasNext()) {
107317             var ss = it.next();
107318             var line = fact.createLineString(ss.getCoordinates());
107319             lines.add(line);
107320           }
107321           return fact.buildGeometry(lines)
107322         };
107323
107324         var ScaledNoder = function ScaledNoder () {
107325           this._noder = null;
107326           this._scaleFactor = null;
107327           this._offsetX = null;
107328           this._offsetY = null;
107329           this._isScaled = false;
107330           if (arguments.length === 2) {
107331             var noder = arguments[0];
107332             var scaleFactor = arguments[1];
107333             this._noder = noder;
107334             this._scaleFactor = scaleFactor;
107335             this._offsetX = 0.0;
107336             this._offsetY = 0.0;
107337             this._isScaled = !this.isIntegerPrecision();
107338           } else if (arguments.length === 4) {
107339             var noder$1 = arguments[0];
107340             var scaleFactor$1 = arguments[1];
107341             var offsetX = arguments[2];
107342             var offsetY = arguments[3];
107343             this._noder = noder$1;
107344             this._scaleFactor = scaleFactor$1;
107345             this._offsetX = offsetX;
107346             this._offsetY = offsetY;
107347             this._isScaled = !this.isIntegerPrecision();
107348           }
107349         };
107350         ScaledNoder.prototype.rescale = function rescale () {
107351             var this$1 = this;
107352
107353           if (hasInterface(arguments[0], Collection)) {
107354             var segStrings = arguments[0];
107355             for (var i = segStrings.iterator(); i.hasNext();) {
107356               var ss = i.next();
107357               this$1.rescale(ss.getCoordinates());
107358             }
107359           } else if (arguments[0] instanceof Array) {
107360             var pts = arguments[0];
107361             // let p0 = null
107362             // let p1 = null
107363             // if (pts.length === 2) {
107364             // p0 = new Coordinate(pts[0])
107365             // p1 = new Coordinate(pts[1])
107366             // }
107367             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107368               pts[i$1].x = pts[i$1].x / this$1._scaleFactor + this$1._offsetX;
107369               pts[i$1].y = pts[i$1].y / this$1._scaleFactor + this$1._offsetY;
107370             }
107371             if (pts.length === 2 && pts[0].equals2D(pts[1])) {
107372               System.out.println(pts);
107373             }
107374           }
107375         };
107376         ScaledNoder.prototype.scale = function scale () {
107377             var this$1 = this;
107378
107379           if (hasInterface(arguments[0], Collection)) {
107380             var segStrings = arguments[0];
107381             var nodedSegmentStrings = new ArrayList();
107382             for (var i = segStrings.iterator(); i.hasNext();) {
107383               var ss = i.next();
107384               nodedSegmentStrings.add(new NodedSegmentString(this$1.scale(ss.getCoordinates()), ss.getData()));
107385             }
107386             return nodedSegmentStrings
107387           } else if (arguments[0] instanceof Array) {
107388             var pts = arguments[0];
107389             var roundPts = new Array(pts.length).fill(null);
107390             for (var i$1 = 0; i$1 < pts.length; i$1++) {
107391               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);
107392             }
107393             var roundPtsNoDup = CoordinateArrays.removeRepeatedPoints(roundPts);
107394             return roundPtsNoDup
107395           }
107396         };
107397         ScaledNoder.prototype.isIntegerPrecision = function isIntegerPrecision () {
107398           return this._scaleFactor === 1.0
107399         };
107400         ScaledNoder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107401           var splitSS = this._noder.getNodedSubstrings();
107402           if (this._isScaled) { this.rescale(splitSS); }
107403           return splitSS
107404         };
107405         ScaledNoder.prototype.computeNodes = function computeNodes (inputSegStrings) {
107406           var intSegStrings = inputSegStrings;
107407           if (this._isScaled) { intSegStrings = this.scale(inputSegStrings); }
107408           this._noder.computeNodes(intSegStrings);
107409         };
107410         ScaledNoder.prototype.interfaces_ = function interfaces_ () {
107411           return [Noder]
107412         };
107413         ScaledNoder.prototype.getClass = function getClass () {
107414           return ScaledNoder
107415         };
107416
107417         var NodingValidator = function NodingValidator () {
107418           this._li = new RobustLineIntersector();
107419           this._segStrings = null;
107420           var segStrings = arguments[0];
107421           this._segStrings = segStrings;
107422         };
107423
107424         var staticAccessors$33 = { fact: { configurable: true } };
107425         NodingValidator.prototype.checkEndPtVertexIntersections = function checkEndPtVertexIntersections () {
107426             var this$1 = this;
107427
107428           if (arguments.length === 0) {
107429             for (var i = this._segStrings.iterator(); i.hasNext();) {
107430               var ss = i.next();
107431               var pts = ss.getCoordinates();
107432               this$1.checkEndPtVertexIntersections(pts[0], this$1._segStrings);
107433               this$1.checkEndPtVertexIntersections(pts[pts.length - 1], this$1._segStrings);
107434             }
107435           } else if (arguments.length === 2) {
107436             var testPt = arguments[0];
107437             var segStrings = arguments[1];
107438             for (var i$1 = segStrings.iterator(); i$1.hasNext();) {
107439               var ss$1 = i$1.next();
107440               var pts$1 = ss$1.getCoordinates();
107441               for (var j = 1; j < pts$1.length - 1; j++) {
107442                 if (pts$1[j].equals(testPt)) { throw new RuntimeException('found endpt/interior pt intersection at index ' + j + ' :pt ' + testPt) }
107443               }
107444             }
107445           }
107446         };
107447         NodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
107448             var this$1 = this;
107449
107450           if (arguments.length === 0) {
107451             for (var i = this._segStrings.iterator(); i.hasNext();) {
107452               var ss0 = i.next();
107453               for (var j = this._segStrings.iterator(); j.hasNext();) {
107454                 var ss1 = j.next();
107455                 this$1.checkInteriorIntersections(ss0, ss1);
107456               }
107457             }
107458           } else if (arguments.length === 2) {
107459             var ss0$1 = arguments[0];
107460             var ss1$1 = arguments[1];
107461             var pts0 = ss0$1.getCoordinates();
107462             var pts1 = ss1$1.getCoordinates();
107463             for (var i0 = 0; i0 < pts0.length - 1; i0++) {
107464               for (var i1 = 0; i1 < pts1.length - 1; i1++) {
107465                 this$1.checkInteriorIntersections(ss0$1, i0, ss1$1, i1);
107466               }
107467             }
107468           } else if (arguments.length === 4) {
107469             var e0 = arguments[0];
107470             var segIndex0 = arguments[1];
107471             var e1 = arguments[2];
107472             var segIndex1 = arguments[3];
107473             if (e0 === e1 && segIndex0 === segIndex1) { return null }
107474             var p00 = e0.getCoordinates()[segIndex0];
107475             var p01 = e0.getCoordinates()[segIndex0 + 1];
107476             var p10 = e1.getCoordinates()[segIndex1];
107477             var p11 = e1.getCoordinates()[segIndex1 + 1];
107478             this._li.computeIntersection(p00, p01, p10, p11);
107479             if (this._li.hasIntersection()) {
107480               if (this._li.isProper() || this.hasInteriorIntersection(this._li, p00, p01) || this.hasInteriorIntersection(this._li, p10, p11)) {
107481                 throw new RuntimeException('found non-noded intersection at ' + p00 + '-' + p01 + ' and ' + p10 + '-' + p11)
107482               }
107483             }
107484           }
107485         };
107486         NodingValidator.prototype.checkValid = function checkValid () {
107487           this.checkEndPtVertexIntersections();
107488           this.checkInteriorIntersections();
107489           this.checkCollapses();
107490         };
107491         NodingValidator.prototype.checkCollapses = function checkCollapses () {
107492             var this$1 = this;
107493
107494           if (arguments.length === 0) {
107495             for (var i = this._segStrings.iterator(); i.hasNext();) {
107496               var ss = i.next();
107497               this$1.checkCollapses(ss);
107498             }
107499           } else if (arguments.length === 1) {
107500             var ss$1 = arguments[0];
107501             var pts = ss$1.getCoordinates();
107502             for (var i$1 = 0; i$1 < pts.length - 2; i$1++) {
107503               this$1.checkCollapse(pts[i$1], pts[i$1 + 1], pts[i$1 + 2]);
107504             }
107505           }
107506         };
107507         NodingValidator.prototype.hasInteriorIntersection = function hasInteriorIntersection (li, p0, p1) {
107508           for (var i = 0; i < li.getIntersectionNum(); i++) {
107509             var intPt = li.getIntersection(i);
107510             if (!(intPt.equals(p0) || intPt.equals(p1))) { return true }
107511           }
107512           return false
107513         };
107514         NodingValidator.prototype.checkCollapse = function checkCollapse (p0, p1, p2) {
107515           if (p0.equals(p2)) { throw new RuntimeException('found non-noded collapse at ' + NodingValidator.fact.createLineString([p0, p1, p2])) }
107516         };
107517         NodingValidator.prototype.interfaces_ = function interfaces_ () {
107518           return []
107519         };
107520         NodingValidator.prototype.getClass = function getClass () {
107521           return NodingValidator
107522         };
107523         staticAccessors$33.fact.get = function () { return new GeometryFactory() };
107524
107525         Object.defineProperties( NodingValidator, staticAccessors$33 );
107526
107527         var HotPixel = function HotPixel () {
107528           this._li = null;
107529           this._pt = null;
107530           this._originalPt = null;
107531           this._ptScaled = null;
107532           this._p0Scaled = null;
107533           this._p1Scaled = null;
107534           this._scaleFactor = null;
107535           this._minx = null;
107536           this._maxx = null;
107537           this._miny = null;
107538           this._maxy = null;
107539           this._corner = new Array(4).fill(null);
107540           this._safeEnv = null;
107541           var pt = arguments[0];
107542           var scaleFactor = arguments[1];
107543           var li = arguments[2];
107544           this._originalPt = pt;
107545           this._pt = pt;
107546           this._scaleFactor = scaleFactor;
107547           this._li = li;
107548           if (scaleFactor <= 0) { throw new IllegalArgumentException('Scale factor must be non-zero') }
107549           if (scaleFactor !== 1.0) {
107550             this._pt = new Coordinate(this.scale(pt.x), this.scale(pt.y));
107551             this._p0Scaled = new Coordinate();
107552             this._p1Scaled = new Coordinate();
107553           }
107554           this.initCorners(this._pt);
107555         };
107556
107557         var staticAccessors$34 = { SAFE_ENV_EXPANSION_FACTOR: { configurable: true } };
107558         HotPixel.prototype.intersectsScaled = function intersectsScaled (p0, p1) {
107559           var segMinx = Math.min(p0.x, p1.x);
107560           var segMaxx = Math.max(p0.x, p1.x);
107561           var segMiny = Math.min(p0.y, p1.y);
107562           var segMaxy = Math.max(p0.y, p1.y);
107563           var isOutsidePixelEnv = this._maxx < segMinx || this._minx > segMaxx || this._maxy < segMiny || this._miny > segMaxy;
107564           if (isOutsidePixelEnv) { return false }
107565           var intersects = this.intersectsToleranceSquare(p0, p1);
107566           Assert.isTrue(!(isOutsidePixelEnv && intersects), 'Found bad envelope test');
107567           return intersects
107568         };
107569         HotPixel.prototype.initCorners = function initCorners (pt) {
107570           var tolerance = 0.5;
107571           this._minx = pt.x - tolerance;
107572           this._maxx = pt.x + tolerance;
107573           this._miny = pt.y - tolerance;
107574           this._maxy = pt.y + tolerance;
107575           this._corner[0] = new Coordinate(this._maxx, this._maxy);
107576           this._corner[1] = new Coordinate(this._minx, this._maxy);
107577           this._corner[2] = new Coordinate(this._minx, this._miny);
107578           this._corner[3] = new Coordinate(this._maxx, this._miny);
107579         };
107580         HotPixel.prototype.intersects = function intersects (p0, p1) {
107581           if (this._scaleFactor === 1.0) { return this.intersectsScaled(p0, p1) }
107582           this.copyScaled(p0, this._p0Scaled);
107583           this.copyScaled(p1, this._p1Scaled);
107584           return this.intersectsScaled(this._p0Scaled, this._p1Scaled)
107585         };
107586         HotPixel.prototype.scale = function scale (val) {
107587           return Math.round(val * this._scaleFactor)
107588         };
107589         HotPixel.prototype.getCoordinate = function getCoordinate () {
107590           return this._originalPt
107591         };
107592         HotPixel.prototype.copyScaled = function copyScaled (p, pScaled) {
107593           pScaled.x = this.scale(p.x);
107594           pScaled.y = this.scale(p.y);
107595         };
107596         HotPixel.prototype.getSafeEnvelope = function getSafeEnvelope () {
107597           if (this._safeEnv === null) {
107598             var safeTolerance = HotPixel.SAFE_ENV_EXPANSION_FACTOR / this._scaleFactor;
107599             this._safeEnv = new Envelope(this._originalPt.x - safeTolerance, this._originalPt.x + safeTolerance, this._originalPt.y - safeTolerance, this._originalPt.y + safeTolerance);
107600           }
107601           return this._safeEnv
107602         };
107603         HotPixel.prototype.intersectsPixelClosure = function intersectsPixelClosure (p0, p1) {
107604           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107605           if (this._li.hasIntersection()) { return true }
107606           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107607           if (this._li.hasIntersection()) { return true }
107608           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107609           if (this._li.hasIntersection()) { return true }
107610           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107611           if (this._li.hasIntersection()) { return true }
107612           return false
107613         };
107614         HotPixel.prototype.intersectsToleranceSquare = function intersectsToleranceSquare (p0, p1) {
107615           var intersectsLeft = false;
107616           var intersectsBottom = false;
107617           this._li.computeIntersection(p0, p1, this._corner[0], this._corner[1]);
107618           if (this._li.isProper()) { return true }
107619           this._li.computeIntersection(p0, p1, this._corner[1], this._corner[2]);
107620           if (this._li.isProper()) { return true }
107621           if (this._li.hasIntersection()) { intersectsLeft = true; }
107622           this._li.computeIntersection(p0, p1, this._corner[2], this._corner[3]);
107623           if (this._li.isProper()) { return true }
107624           if (this._li.hasIntersection()) { intersectsBottom = true; }
107625           this._li.computeIntersection(p0, p1, this._corner[3], this._corner[0]);
107626           if (this._li.isProper()) { return true }
107627           if (intersectsLeft && intersectsBottom) { return true }
107628           if (p0.equals(this._pt)) { return true }
107629           if (p1.equals(this._pt)) { return true }
107630           return false
107631         };
107632         HotPixel.prototype.addSnappedNode = function addSnappedNode (segStr, segIndex) {
107633           var p0 = segStr.getCoordinate(segIndex);
107634           var p1 = segStr.getCoordinate(segIndex + 1);
107635           if (this.intersects(p0, p1)) {
107636             segStr.addIntersection(this.getCoordinate(), segIndex);
107637             return true
107638           }
107639           return false
107640         };
107641         HotPixel.prototype.interfaces_ = function interfaces_ () {
107642           return []
107643         };
107644         HotPixel.prototype.getClass = function getClass () {
107645           return HotPixel
107646         };
107647         staticAccessors$34.SAFE_ENV_EXPANSION_FACTOR.get = function () { return 0.75 };
107648
107649         Object.defineProperties( HotPixel, staticAccessors$34 );
107650
107651         var MonotoneChainSelectAction = function MonotoneChainSelectAction () {
107652           this.tempEnv1 = new Envelope();
107653           this.selectedSegment = new LineSegment();
107654         };
107655         MonotoneChainSelectAction.prototype.select = function select () {
107656           if (arguments.length === 1) ; else if (arguments.length === 2) {
107657             var mc = arguments[0];
107658             var startIndex = arguments[1];
107659             mc.getLineSegment(startIndex, this.selectedSegment);
107660             this.select(this.selectedSegment);
107661           }
107662         };
107663         MonotoneChainSelectAction.prototype.interfaces_ = function interfaces_ () {
107664           return []
107665         };
107666         MonotoneChainSelectAction.prototype.getClass = function getClass () {
107667           return MonotoneChainSelectAction
107668         };
107669
107670         var MCIndexPointSnapper = function MCIndexPointSnapper () {
107671           this._index = null;
107672           var index = arguments[0];
107673           this._index = index;
107674         };
107675
107676         var staticAccessors$35 = { HotPixelSnapAction: { configurable: true } };
107677         MCIndexPointSnapper.prototype.snap = function snap () {
107678           if (arguments.length === 1) {
107679             var hotPixel = arguments[0];
107680             return this.snap(hotPixel, null, -1)
107681           } else if (arguments.length === 3) {
107682             var hotPixel$1 = arguments[0];
107683             var parentEdge = arguments[1];
107684             var hotPixelVertexIndex = arguments[2];
107685             var pixelEnv = hotPixel$1.getSafeEnvelope();
107686             var hotPixelSnapAction = new HotPixelSnapAction(hotPixel$1, parentEdge, hotPixelVertexIndex);
107687             this._index.query(pixelEnv, {
107688               interfaces_: function () {
107689                 return [ItemVisitor]
107690               },
107691               visitItem: function (item) {
107692                 var testChain = item;
107693                 testChain.select(pixelEnv, hotPixelSnapAction);
107694               }
107695             });
107696             return hotPixelSnapAction.isNodeAdded()
107697           }
107698         };
107699         MCIndexPointSnapper.prototype.interfaces_ = function interfaces_ () {
107700           return []
107701         };
107702         MCIndexPointSnapper.prototype.getClass = function getClass () {
107703           return MCIndexPointSnapper
107704         };
107705         staticAccessors$35.HotPixelSnapAction.get = function () { return HotPixelSnapAction };
107706
107707         Object.defineProperties( MCIndexPointSnapper, staticAccessors$35 );
107708
107709         var HotPixelSnapAction = (function (MonotoneChainSelectAction$$1) {
107710           function HotPixelSnapAction () {
107711             MonotoneChainSelectAction$$1.call(this);
107712             this._hotPixel = null;
107713             this._parentEdge = null;
107714             this._hotPixelVertexIndex = null;
107715             this._isNodeAdded = false;
107716             var hotPixel = arguments[0];
107717             var parentEdge = arguments[1];
107718             var hotPixelVertexIndex = arguments[2];
107719             this._hotPixel = hotPixel;
107720             this._parentEdge = parentEdge;
107721             this._hotPixelVertexIndex = hotPixelVertexIndex;
107722           }
107723
107724           if ( MonotoneChainSelectAction$$1 ) { HotPixelSnapAction.__proto__ = MonotoneChainSelectAction$$1; }
107725           HotPixelSnapAction.prototype = Object.create( MonotoneChainSelectAction$$1 && MonotoneChainSelectAction$$1.prototype );
107726           HotPixelSnapAction.prototype.constructor = HotPixelSnapAction;
107727           HotPixelSnapAction.prototype.isNodeAdded = function isNodeAdded () {
107728             return this._isNodeAdded
107729           };
107730           HotPixelSnapAction.prototype.select = function select () {
107731             if (arguments.length === 2) {
107732               var mc = arguments[0];
107733               var startIndex = arguments[1];
107734               var ss = mc.getContext();
107735               if (this._parentEdge !== null) {
107736                 if (ss === this._parentEdge && startIndex === this._hotPixelVertexIndex) { return null }
107737               }
107738               this._isNodeAdded = this._hotPixel.addSnappedNode(ss, startIndex);
107739             } else { return MonotoneChainSelectAction$$1.prototype.select.apply(this, arguments) }
107740           };
107741           HotPixelSnapAction.prototype.interfaces_ = function interfaces_ () {
107742             return []
107743           };
107744           HotPixelSnapAction.prototype.getClass = function getClass () {
107745             return HotPixelSnapAction
107746           };
107747
107748           return HotPixelSnapAction;
107749         }(MonotoneChainSelectAction));
107750
107751         var InteriorIntersectionFinderAdder = function InteriorIntersectionFinderAdder () {
107752           this._li = null;
107753           this._interiorIntersections = null;
107754           var li = arguments[0];
107755           this._li = li;
107756           this._interiorIntersections = new ArrayList();
107757         };
107758         InteriorIntersectionFinderAdder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
107759             var this$1 = this;
107760
107761           if (e0 === e1 && segIndex0 === segIndex1) { return null }
107762           var p00 = e0.getCoordinates()[segIndex0];
107763           var p01 = e0.getCoordinates()[segIndex0 + 1];
107764           var p10 = e1.getCoordinates()[segIndex1];
107765           var p11 = e1.getCoordinates()[segIndex1 + 1];
107766           this._li.computeIntersection(p00, p01, p10, p11);
107767           if (this._li.hasIntersection()) {
107768             if (this._li.isInteriorIntersection()) {
107769               for (var intIndex = 0; intIndex < this._li.getIntersectionNum(); intIndex++) {
107770                 this$1._interiorIntersections.add(this$1._li.getIntersection(intIndex));
107771               }
107772               e0.addIntersections(this._li, segIndex0, 0);
107773               e1.addIntersections(this._li, segIndex1, 1);
107774             }
107775           }
107776         };
107777         InteriorIntersectionFinderAdder.prototype.isDone = function isDone () {
107778           return false
107779         };
107780         InteriorIntersectionFinderAdder.prototype.getInteriorIntersections = function getInteriorIntersections () {
107781           return this._interiorIntersections
107782         };
107783         InteriorIntersectionFinderAdder.prototype.interfaces_ = function interfaces_ () {
107784           return [SegmentIntersector]
107785         };
107786         InteriorIntersectionFinderAdder.prototype.getClass = function getClass () {
107787           return InteriorIntersectionFinderAdder
107788         };
107789
107790         var MCIndexSnapRounder = function MCIndexSnapRounder () {
107791           this._pm = null;
107792           this._li = null;
107793           this._scaleFactor = null;
107794           this._noder = null;
107795           this._pointSnapper = null;
107796           this._nodedSegStrings = null;
107797           var pm = arguments[0];
107798           this._pm = pm;
107799           this._li = new RobustLineIntersector();
107800           this._li.setPrecisionModel(pm);
107801           this._scaleFactor = pm.getScale();
107802         };
107803         MCIndexSnapRounder.prototype.checkCorrectness = function checkCorrectness (inputSegmentStrings) {
107804           var resultSegStrings = NodedSegmentString.getNodedSubstrings(inputSegmentStrings);
107805           var nv = new NodingValidator(resultSegStrings);
107806           try {
107807             nv.checkValid();
107808           } catch (ex) {
107809             if (ex instanceof Exception) {
107810               ex.printStackTrace();
107811             } else { throw ex }
107812           } finally {}
107813         };
107814         MCIndexSnapRounder.prototype.getNodedSubstrings = function getNodedSubstrings () {
107815           return NodedSegmentString.getNodedSubstrings(this._nodedSegStrings)
107816         };
107817         MCIndexSnapRounder.prototype.snapRound = function snapRound (segStrings, li) {
107818           var intersections = this.findInteriorIntersections(segStrings, li);
107819           this.computeIntersectionSnaps(intersections);
107820           this.computeVertexSnaps(segStrings);
107821         };
107822         MCIndexSnapRounder.prototype.findInteriorIntersections = function findInteriorIntersections (segStrings, li) {
107823           var intFinderAdder = new InteriorIntersectionFinderAdder(li);
107824           this._noder.setSegmentIntersector(intFinderAdder);
107825           this._noder.computeNodes(segStrings);
107826           return intFinderAdder.getInteriorIntersections()
107827         };
107828         MCIndexSnapRounder.prototype.computeVertexSnaps = function computeVertexSnaps () {
107829             var this$1 = this;
107830
107831           if (hasInterface(arguments[0], Collection)) {
107832             var edges = arguments[0];
107833             for (var i0 = edges.iterator(); i0.hasNext();) {
107834               var edge0 = i0.next();
107835               this$1.computeVertexSnaps(edge0);
107836             }
107837           } else if (arguments[0] instanceof NodedSegmentString) {
107838             var e = arguments[0];
107839             var pts0 = e.getCoordinates();
107840             for (var i = 0; i < pts0.length; i++) {
107841               var hotPixel = new HotPixel(pts0[i], this$1._scaleFactor, this$1._li);
107842               var isNodeAdded = this$1._pointSnapper.snap(hotPixel, e, i);
107843               if (isNodeAdded) {
107844                 e.addIntersection(pts0[i], i);
107845               }
107846             }
107847           }
107848         };
107849         MCIndexSnapRounder.prototype.computeNodes = function computeNodes (inputSegmentStrings) {
107850           this._nodedSegStrings = inputSegmentStrings;
107851           this._noder = new MCIndexNoder();
107852           this._pointSnapper = new MCIndexPointSnapper(this._noder.getIndex());
107853           this.snapRound(inputSegmentStrings, this._li);
107854         };
107855         MCIndexSnapRounder.prototype.computeIntersectionSnaps = function computeIntersectionSnaps (snapPts) {
107856             var this$1 = this;
107857
107858           for (var it = snapPts.iterator(); it.hasNext();) {
107859             var snapPt = it.next();
107860             var hotPixel = new HotPixel(snapPt, this$1._scaleFactor, this$1._li);
107861             this$1._pointSnapper.snap(hotPixel);
107862           }
107863         };
107864         MCIndexSnapRounder.prototype.interfaces_ = function interfaces_ () {
107865           return [Noder]
107866         };
107867         MCIndexSnapRounder.prototype.getClass = function getClass () {
107868           return MCIndexSnapRounder
107869         };
107870
107871         var BufferOp = function BufferOp () {
107872           this._argGeom = null;
107873           this._distance = null;
107874           this._bufParams = new BufferParameters();
107875           this._resultGeometry = null;
107876           this._saveException = null;
107877           if (arguments.length === 1) {
107878             var g = arguments[0];
107879             this._argGeom = g;
107880           } else if (arguments.length === 2) {
107881             var g$1 = arguments[0];
107882             var bufParams = arguments[1];
107883             this._argGeom = g$1;
107884             this._bufParams = bufParams;
107885           }
107886         };
107887
107888         var staticAccessors$32 = { CAP_ROUND: { configurable: true },CAP_BUTT: { configurable: true },CAP_FLAT: { configurable: true },CAP_SQUARE: { configurable: true },MAX_PRECISION_DIGITS: { configurable: true } };
107889         BufferOp.prototype.bufferFixedPrecision = function bufferFixedPrecision (fixedPM) {
107890           var noder = new ScaledNoder(new MCIndexSnapRounder(new PrecisionModel(1.0)), fixedPM.getScale());
107891           var bufBuilder = new BufferBuilder(this._bufParams);
107892           bufBuilder.setWorkingPrecisionModel(fixedPM);
107893           bufBuilder.setNoder(noder);
107894           this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107895         };
107896         BufferOp.prototype.bufferReducedPrecision = function bufferReducedPrecision () {
107897             var this$1 = this;
107898
107899           if (arguments.length === 0) {
107900             for (var precDigits = BufferOp.MAX_PRECISION_DIGITS; precDigits >= 0; precDigits--) {
107901               try {
107902                 this$1.bufferReducedPrecision(precDigits);
107903               } catch (ex) {
107904                 if (ex instanceof TopologyException) {
107905                   this$1._saveException = ex;
107906                 } else { throw ex }
107907               } finally {}
107908               if (this$1._resultGeometry !== null) { return null }
107909             }
107910             throw this._saveException
107911           } else if (arguments.length === 1) {
107912             var precisionDigits = arguments[0];
107913             var sizeBasedScaleFactor = BufferOp.precisionScaleFactor(this._argGeom, this._distance, precisionDigits);
107914             var fixedPM = new PrecisionModel(sizeBasedScaleFactor);
107915             this.bufferFixedPrecision(fixedPM);
107916           }
107917         };
107918         BufferOp.prototype.computeGeometry = function computeGeometry () {
107919           this.bufferOriginalPrecision();
107920           if (this._resultGeometry !== null) { return null }
107921           var argPM = this._argGeom.getFactory().getPrecisionModel();
107922           if (argPM.getType() === PrecisionModel.FIXED) { this.bufferFixedPrecision(argPM); } else { this.bufferReducedPrecision(); }
107923         };
107924         BufferOp.prototype.setQuadrantSegments = function setQuadrantSegments (quadrantSegments) {
107925           this._bufParams.setQuadrantSegments(quadrantSegments);
107926         };
107927         BufferOp.prototype.bufferOriginalPrecision = function bufferOriginalPrecision () {
107928           try {
107929             var bufBuilder = new BufferBuilder(this._bufParams);
107930             this._resultGeometry = bufBuilder.buffer(this._argGeom, this._distance);
107931           } catch (ex) {
107932             if (ex instanceof RuntimeException) {
107933               this._saveException = ex;
107934             } else { throw ex }
107935           } finally {}
107936         };
107937         BufferOp.prototype.getResultGeometry = function getResultGeometry (distance) {
107938           this._distance = distance;
107939           this.computeGeometry();
107940           return this._resultGeometry
107941         };
107942         BufferOp.prototype.setEndCapStyle = function setEndCapStyle (endCapStyle) {
107943           this._bufParams.setEndCapStyle(endCapStyle);
107944         };
107945         BufferOp.prototype.interfaces_ = function interfaces_ () {
107946           return []
107947         };
107948         BufferOp.prototype.getClass = function getClass () {
107949           return BufferOp
107950         };
107951         BufferOp.bufferOp = function bufferOp () {
107952           if (arguments.length === 2) {
107953             var g = arguments[0];
107954             var distance = arguments[1];
107955             var gBuf = new BufferOp(g);
107956             var geomBuf = gBuf.getResultGeometry(distance);
107957             return geomBuf
107958           } else if (arguments.length === 3) {
107959             if (Number.isInteger(arguments[2]) && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107960               var g$1 = arguments[0];
107961               var distance$1 = arguments[1];
107962               var quadrantSegments = arguments[2];
107963               var bufOp = new BufferOp(g$1);
107964               bufOp.setQuadrantSegments(quadrantSegments);
107965               var geomBuf$1 = bufOp.getResultGeometry(distance$1);
107966               return geomBuf$1
107967             } else if (arguments[2] instanceof BufferParameters && (arguments[0] instanceof Geometry && typeof arguments[1] === 'number')) {
107968               var g$2 = arguments[0];
107969               var distance$2 = arguments[1];
107970               var params = arguments[2];
107971               var bufOp$1 = new BufferOp(g$2, params);
107972               var geomBuf$2 = bufOp$1.getResultGeometry(distance$2);
107973               return geomBuf$2
107974             }
107975           } else if (arguments.length === 4) {
107976             var g$3 = arguments[0];
107977             var distance$3 = arguments[1];
107978             var quadrantSegments$1 = arguments[2];
107979             var endCapStyle = arguments[3];
107980             var bufOp$2 = new BufferOp(g$3);
107981             bufOp$2.setQuadrantSegments(quadrantSegments$1);
107982             bufOp$2.setEndCapStyle(endCapStyle);
107983             var geomBuf$3 = bufOp$2.getResultGeometry(distance$3);
107984             return geomBuf$3
107985           }
107986         };
107987         BufferOp.precisionScaleFactor = function precisionScaleFactor (g, distance, maxPrecisionDigits) {
107988           var env = g.getEnvelopeInternal();
107989           var envMax = MathUtil.max(Math.abs(env.getMaxX()), Math.abs(env.getMaxY()), Math.abs(env.getMinX()), Math.abs(env.getMinY()));
107990           var expandByDistance = distance > 0.0 ? distance : 0.0;
107991           var bufEnvMax = envMax + 2 * expandByDistance;
107992           var bufEnvPrecisionDigits = Math.trunc(Math.log(bufEnvMax) / Math.log(10) + 1.0);
107993           var minUnitLog10 = maxPrecisionDigits - bufEnvPrecisionDigits;
107994           var scaleFactor = Math.pow(10.0, minUnitLog10);
107995           return scaleFactor
107996         };
107997         staticAccessors$32.CAP_ROUND.get = function () { return BufferParameters.CAP_ROUND };
107998         staticAccessors$32.CAP_BUTT.get = function () { return BufferParameters.CAP_FLAT };
107999         staticAccessors$32.CAP_FLAT.get = function () { return BufferParameters.CAP_FLAT };
108000         staticAccessors$32.CAP_SQUARE.get = function () { return BufferParameters.CAP_SQUARE };
108001         staticAccessors$32.MAX_PRECISION_DIGITS.get = function () { return 12 };
108002
108003         Object.defineProperties( BufferOp, staticAccessors$32 );
108004
108005         var PointPairDistance = function PointPairDistance () {
108006           this._pt = [new Coordinate(), new Coordinate()];
108007           this._distance = Double.NaN;
108008           this._isNull = true;
108009         };
108010         PointPairDistance.prototype.getCoordinates = function getCoordinates () {
108011           return this._pt
108012         };
108013         PointPairDistance.prototype.getCoordinate = function getCoordinate (i) {
108014           return this._pt[i]
108015         };
108016         PointPairDistance.prototype.setMinimum = function setMinimum () {
108017           if (arguments.length === 1) {
108018             var ptDist = arguments[0];
108019             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108020           } else if (arguments.length === 2) {
108021             var p0 = arguments[0];
108022             var p1 = arguments[1];
108023             if (this._isNull) {
108024               this.initialize(p0, p1);
108025               return null
108026             }
108027             var dist = p0.distance(p1);
108028             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108029           }
108030         };
108031         PointPairDistance.prototype.initialize = function initialize () {
108032           if (arguments.length === 0) {
108033             this._isNull = true;
108034           } else if (arguments.length === 2) {
108035             var p0 = arguments[0];
108036             var p1 = arguments[1];
108037             this._pt[0].setCoordinate(p0);
108038             this._pt[1].setCoordinate(p1);
108039             this._distance = p0.distance(p1);
108040             this._isNull = false;
108041           } else if (arguments.length === 3) {
108042             var p0$1 = arguments[0];
108043             var p1$1 = arguments[1];
108044             var distance = arguments[2];
108045             this._pt[0].setCoordinate(p0$1);
108046             this._pt[1].setCoordinate(p1$1);
108047             this._distance = distance;
108048             this._isNull = false;
108049           }
108050         };
108051         PointPairDistance.prototype.getDistance = function getDistance () {
108052           return this._distance
108053         };
108054         PointPairDistance.prototype.setMaximum = function setMaximum () {
108055           if (arguments.length === 1) {
108056             var ptDist = arguments[0];
108057             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108058           } else if (arguments.length === 2) {
108059             var p0 = arguments[0];
108060             var p1 = arguments[1];
108061             if (this._isNull) {
108062               this.initialize(p0, p1);
108063               return null
108064             }
108065             var dist = p0.distance(p1);
108066             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108067           }
108068         };
108069         PointPairDistance.prototype.interfaces_ = function interfaces_ () {
108070           return []
108071         };
108072         PointPairDistance.prototype.getClass = function getClass () {
108073           return PointPairDistance
108074         };
108075
108076         var DistanceToPointFinder = function DistanceToPointFinder () {};
108077
108078         DistanceToPointFinder.prototype.interfaces_ = function interfaces_ () {
108079           return []
108080         };
108081         DistanceToPointFinder.prototype.getClass = function getClass () {
108082           return DistanceToPointFinder
108083         };
108084         DistanceToPointFinder.computeDistance = function computeDistance () {
108085           if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108086             var line = arguments[0];
108087             var pt = arguments[1];
108088             var ptDist = arguments[2];
108089             var coords = line.getCoordinates();
108090             var tempSegment = new LineSegment();
108091             for (var i = 0; i < coords.length - 1; i++) {
108092               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108093               var closestPt = tempSegment.closestPoint(pt);
108094               ptDist.setMinimum(closestPt, pt);
108095             }
108096           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108097             var poly = arguments[0];
108098             var pt$1 = arguments[1];
108099             var ptDist$1 = arguments[2];
108100             DistanceToPointFinder.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108101             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108102               DistanceToPointFinder.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108103             }
108104           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108105             var geom = arguments[0];
108106             var pt$2 = arguments[1];
108107             var ptDist$2 = arguments[2];
108108             if (geom instanceof LineString) {
108109               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108110             } else if (geom instanceof Polygon) {
108111               DistanceToPointFinder.computeDistance(geom, pt$2, ptDist$2);
108112             } else if (geom instanceof GeometryCollection) {
108113               var gc = geom;
108114               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108115                 var g = gc.getGeometryN(i$2);
108116                 DistanceToPointFinder.computeDistance(g, pt$2, ptDist$2);
108117               }
108118             } else {
108119               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108120             }
108121           } else if (arguments[2] instanceof PointPairDistance && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108122             var segment = arguments[0];
108123             var pt$3 = arguments[1];
108124             var ptDist$3 = arguments[2];
108125             var closestPt$1 = segment.closestPoint(pt$3);
108126             ptDist$3.setMinimum(closestPt$1, pt$3);
108127           }
108128         };
108129
108130         var BufferCurveMaximumDistanceFinder = function BufferCurveMaximumDistanceFinder (inputGeom) {
108131           this._maxPtDist = new PointPairDistance();
108132           this._inputGeom = inputGeom || null;
108133         };
108134
108135         var staticAccessors$36 = { MaxPointDistanceFilter: { configurable: true },MaxMidpointDistanceFilter: { configurable: true } };
108136         BufferCurveMaximumDistanceFinder.prototype.computeMaxMidpointDistance = function computeMaxMidpointDistance (curve) {
108137           var distFilter = new MaxMidpointDistanceFilter(this._inputGeom);
108138           curve.apply(distFilter);
108139           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108140         };
108141         BufferCurveMaximumDistanceFinder.prototype.computeMaxVertexDistance = function computeMaxVertexDistance (curve) {
108142           var distFilter = new MaxPointDistanceFilter(this._inputGeom);
108143           curve.apply(distFilter);
108144           this._maxPtDist.setMaximum(distFilter.getMaxPointDistance());
108145         };
108146         BufferCurveMaximumDistanceFinder.prototype.findDistance = function findDistance (bufferCurve) {
108147           this.computeMaxVertexDistance(bufferCurve);
108148           this.computeMaxMidpointDistance(bufferCurve);
108149           return this._maxPtDist.getDistance()
108150         };
108151         BufferCurveMaximumDistanceFinder.prototype.getDistancePoints = function getDistancePoints () {
108152           return this._maxPtDist
108153         };
108154         BufferCurveMaximumDistanceFinder.prototype.interfaces_ = function interfaces_ () {
108155           return []
108156         };
108157         BufferCurveMaximumDistanceFinder.prototype.getClass = function getClass () {
108158           return BufferCurveMaximumDistanceFinder
108159         };
108160         staticAccessors$36.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter };
108161         staticAccessors$36.MaxMidpointDistanceFilter.get = function () { return MaxMidpointDistanceFilter };
108162
108163         Object.defineProperties( BufferCurveMaximumDistanceFinder, staticAccessors$36 );
108164
108165         var MaxPointDistanceFilter = function MaxPointDistanceFilter (geom) {
108166           this._maxPtDist = new PointPairDistance();
108167           this._minPtDist = new PointPairDistance();
108168           this._geom = geom || null;
108169         };
108170         MaxPointDistanceFilter.prototype.filter = function filter (pt) {
108171           this._minPtDist.initialize();
108172           DistanceToPointFinder.computeDistance(this._geom, pt, this._minPtDist);
108173           this._maxPtDist.setMaximum(this._minPtDist);
108174         };
108175         MaxPointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108176           return this._maxPtDist
108177         };
108178         MaxPointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108179           return [CoordinateFilter]
108180         };
108181         MaxPointDistanceFilter.prototype.getClass = function getClass () {
108182           return MaxPointDistanceFilter
108183         };
108184
108185         var MaxMidpointDistanceFilter = function MaxMidpointDistanceFilter (geom) {
108186           this._maxPtDist = new PointPairDistance();
108187           this._minPtDist = new PointPairDistance();
108188           this._geom = geom || null;
108189         };
108190         MaxMidpointDistanceFilter.prototype.filter = function filter (seq, index) {
108191           if (index === 0) { return null }
108192           var p0 = seq.getCoordinate(index - 1);
108193           var p1 = seq.getCoordinate(index);
108194           var midPt = new Coordinate((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
108195           this._minPtDist.initialize();
108196           DistanceToPointFinder.computeDistance(this._geom, midPt, this._minPtDist);
108197           this._maxPtDist.setMaximum(this._minPtDist);
108198         };
108199         MaxMidpointDistanceFilter.prototype.isDone = function isDone () {
108200           return false
108201         };
108202         MaxMidpointDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
108203           return false
108204         };
108205         MaxMidpointDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
108206           return this._maxPtDist
108207         };
108208         MaxMidpointDistanceFilter.prototype.interfaces_ = function interfaces_ () {
108209           return [CoordinateSequenceFilter]
108210         };
108211         MaxMidpointDistanceFilter.prototype.getClass = function getClass () {
108212           return MaxMidpointDistanceFilter
108213         };
108214
108215         var PolygonExtracter = function PolygonExtracter (comps) {
108216           this._comps = comps || null;
108217         };
108218         PolygonExtracter.prototype.filter = function filter (geom) {
108219           if (geom instanceof Polygon) { this._comps.add(geom); }
108220         };
108221         PolygonExtracter.prototype.interfaces_ = function interfaces_ () {
108222           return [GeometryFilter]
108223         };
108224         PolygonExtracter.prototype.getClass = function getClass () {
108225           return PolygonExtracter
108226         };
108227         PolygonExtracter.getPolygons = function getPolygons () {
108228           if (arguments.length === 1) {
108229             var geom = arguments[0];
108230             return PolygonExtracter.getPolygons(geom, new ArrayList())
108231           } else if (arguments.length === 2) {
108232             var geom$1 = arguments[0];
108233             var list = arguments[1];
108234             if (geom$1 instanceof Polygon) {
108235               list.add(geom$1);
108236             } else if (geom$1 instanceof GeometryCollection) {
108237               geom$1.apply(new PolygonExtracter(list));
108238             }
108239             return list
108240           }
108241         };
108242
108243         var LinearComponentExtracter = function LinearComponentExtracter () {
108244           this._lines = null;
108245           this._isForcedToLineString = false;
108246           if (arguments.length === 1) {
108247             var lines = arguments[0];
108248             this._lines = lines;
108249           } else if (arguments.length === 2) {
108250             var lines$1 = arguments[0];
108251             var isForcedToLineString = arguments[1];
108252             this._lines = lines$1;
108253             this._isForcedToLineString = isForcedToLineString;
108254           }
108255         };
108256         LinearComponentExtracter.prototype.filter = function filter (geom) {
108257           if (this._isForcedToLineString && geom instanceof LinearRing) {
108258             var line = geom.getFactory().createLineString(geom.getCoordinateSequence());
108259             this._lines.add(line);
108260             return null
108261           }
108262           if (geom instanceof LineString) { this._lines.add(geom); }
108263         };
108264         LinearComponentExtracter.prototype.setForceToLineString = function setForceToLineString (isForcedToLineString) {
108265           this._isForcedToLineString = isForcedToLineString;
108266         };
108267         LinearComponentExtracter.prototype.interfaces_ = function interfaces_ () {
108268           return [GeometryComponentFilter]
108269         };
108270         LinearComponentExtracter.prototype.getClass = function getClass () {
108271           return LinearComponentExtracter
108272         };
108273         LinearComponentExtracter.getGeometry = function getGeometry () {
108274           if (arguments.length === 1) {
108275             var geom = arguments[0];
108276             return geom.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom))
108277           } else if (arguments.length === 2) {
108278             var geom$1 = arguments[0];
108279             var forceToLineString = arguments[1];
108280             return geom$1.getFactory().buildGeometry(LinearComponentExtracter.getLines(geom$1, forceToLineString))
108281           }
108282         };
108283         LinearComponentExtracter.getLines = function getLines () {
108284           if (arguments.length === 1) {
108285             var geom = arguments[0];
108286             return LinearComponentExtracter.getLines(geom, false)
108287           } else if (arguments.length === 2) {
108288             if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection)) {
108289               var geoms = arguments[0];
108290               var lines$1 = arguments[1];
108291               for (var i = geoms.iterator(); i.hasNext();) {
108292                 var g = i.next();
108293                 LinearComponentExtracter.getLines(g, lines$1);
108294               }
108295               return lines$1
108296             } else if (arguments[0] instanceof Geometry && typeof arguments[1] === 'boolean') {
108297               var geom$1 = arguments[0];
108298               var forceToLineString = arguments[1];
108299               var lines = new ArrayList();
108300               geom$1.apply(new LinearComponentExtracter(lines, forceToLineString));
108301               return lines
108302             } else if (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection)) {
108303               var geom$2 = arguments[0];
108304               var lines$2 = arguments[1];
108305               if (geom$2 instanceof LineString) {
108306                 lines$2.add(geom$2);
108307               } else {
108308                 geom$2.apply(new LinearComponentExtracter(lines$2));
108309               }
108310               return lines$2
108311             }
108312           } else if (arguments.length === 3) {
108313             if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], Collection))) {
108314               var geoms$1 = arguments[0];
108315               var lines$3 = arguments[1];
108316               var forceToLineString$1 = arguments[2];
108317               for (var i$1 = geoms$1.iterator(); i$1.hasNext();) {
108318                 var g$1 = i$1.next();
108319                 LinearComponentExtracter.getLines(g$1, lines$3, forceToLineString$1);
108320               }
108321               return lines$3
108322             } else if (typeof arguments[2] === 'boolean' && (arguments[0] instanceof Geometry && hasInterface(arguments[1], Collection))) {
108323               var geom$3 = arguments[0];
108324               var lines$4 = arguments[1];
108325               var forceToLineString$2 = arguments[2];
108326               geom$3.apply(new LinearComponentExtracter(lines$4, forceToLineString$2));
108327               return lines$4
108328             }
108329           }
108330         };
108331
108332         var PointLocator = function PointLocator () {
108333           this._boundaryRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
108334           this._isIn = null;
108335           this._numBoundaries = null;
108336           if (arguments.length === 0) ; else if (arguments.length === 1) {
108337             var boundaryRule = arguments[0];
108338             if (boundaryRule === null) { throw new IllegalArgumentException('Rule must be non-null') }
108339             this._boundaryRule = boundaryRule;
108340           }
108341         };
108342         PointLocator.prototype.locateInternal = function locateInternal () {
108343             var this$1 = this;
108344
108345           if (arguments[0] instanceof Coordinate && arguments[1] instanceof Polygon) {
108346             var p = arguments[0];
108347             var poly = arguments[1];
108348             if (poly.isEmpty()) { return Location.EXTERIOR }
108349             var shell = poly.getExteriorRing();
108350             var shellLoc = this.locateInPolygonRing(p, shell);
108351             if (shellLoc === Location.EXTERIOR) { return Location.EXTERIOR }
108352             if (shellLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108353             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
108354               var hole = poly.getInteriorRingN(i);
108355               var holeLoc = this$1.locateInPolygonRing(p, hole);
108356               if (holeLoc === Location.INTERIOR) { return Location.EXTERIOR }
108357               if (holeLoc === Location.BOUNDARY) { return Location.BOUNDARY }
108358             }
108359             return Location.INTERIOR
108360           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof LineString) {
108361             var p$1 = arguments[0];
108362             var l = arguments[1];
108363             if (!l.getEnvelopeInternal().intersects(p$1)) { return Location.EXTERIOR }
108364             var pt = l.getCoordinates();
108365             if (!l.isClosed()) {
108366               if (p$1.equals(pt[0]) || p$1.equals(pt[pt.length - 1])) {
108367                 return Location.BOUNDARY
108368               }
108369             }
108370             if (CGAlgorithms.isOnLine(p$1, pt)) { return Location.INTERIOR }
108371             return Location.EXTERIOR
108372           } else if (arguments[0] instanceof Coordinate && arguments[1] instanceof Point$1) {
108373             var p$2 = arguments[0];
108374             var pt$1 = arguments[1];
108375             var ptCoord = pt$1.getCoordinate();
108376             if (ptCoord.equals2D(p$2)) { return Location.INTERIOR }
108377             return Location.EXTERIOR
108378           }
108379         };
108380         PointLocator.prototype.locateInPolygonRing = function locateInPolygonRing (p, ring) {
108381           if (!ring.getEnvelopeInternal().intersects(p)) { return Location.EXTERIOR }
108382           return CGAlgorithms.locatePointInRing(p, ring.getCoordinates())
108383         };
108384         PointLocator.prototype.intersects = function intersects (p, geom) {
108385           return this.locate(p, geom) !== Location.EXTERIOR
108386         };
108387         PointLocator.prototype.updateLocationInfo = function updateLocationInfo (loc) {
108388           if (loc === Location.INTERIOR) { this._isIn = true; }
108389           if (loc === Location.BOUNDARY) { this._numBoundaries++; }
108390         };
108391         PointLocator.prototype.computeLocation = function computeLocation (p, geom) {
108392             var this$1 = this;
108393
108394           if (geom instanceof Point$1) {
108395             this.updateLocationInfo(this.locateInternal(p, geom));
108396           }
108397           if (geom instanceof LineString) {
108398             this.updateLocationInfo(this.locateInternal(p, geom));
108399           } else if (geom instanceof Polygon) {
108400             this.updateLocationInfo(this.locateInternal(p, geom));
108401           } else if (geom instanceof MultiLineString) {
108402             var ml = geom;
108403             for (var i = 0; i < ml.getNumGeometries(); i++) {
108404               var l = ml.getGeometryN(i);
108405               this$1.updateLocationInfo(this$1.locateInternal(p, l));
108406             }
108407           } else if (geom instanceof MultiPolygon) {
108408             var mpoly = geom;
108409             for (var i$1 = 0; i$1 < mpoly.getNumGeometries(); i$1++) {
108410               var poly = mpoly.getGeometryN(i$1);
108411               this$1.updateLocationInfo(this$1.locateInternal(p, poly));
108412             }
108413           } else if (geom instanceof GeometryCollection) {
108414             var geomi = new GeometryCollectionIterator(geom);
108415             while (geomi.hasNext()) {
108416               var g2 = geomi.next();
108417               if (g2 !== geom) { this$1.computeLocation(p, g2); }
108418             }
108419           }
108420         };
108421         PointLocator.prototype.locate = function locate (p, geom) {
108422           if (geom.isEmpty()) { return Location.EXTERIOR }
108423           if (geom instanceof LineString) {
108424             return this.locateInternal(p, geom)
108425           } else if (geom instanceof Polygon) {
108426             return this.locateInternal(p, geom)
108427           }
108428           this._isIn = false;
108429           this._numBoundaries = 0;
108430           this.computeLocation(p, geom);
108431           if (this._boundaryRule.isInBoundary(this._numBoundaries)) { return Location.BOUNDARY }
108432           if (this._numBoundaries > 0 || this._isIn) { return Location.INTERIOR }
108433           return Location.EXTERIOR
108434         };
108435         PointLocator.prototype.interfaces_ = function interfaces_ () {
108436           return []
108437         };
108438         PointLocator.prototype.getClass = function getClass () {
108439           return PointLocator
108440         };
108441
108442         var GeometryLocation = function GeometryLocation () {
108443           this._component = null;
108444           this._segIndex = null;
108445           this._pt = null;
108446           if (arguments.length === 2) {
108447             var component = arguments[0];
108448             var pt = arguments[1];
108449             GeometryLocation.call(this, component, GeometryLocation.INSIDE_AREA, pt);
108450           } else if (arguments.length === 3) {
108451             var component$1 = arguments[0];
108452             var segIndex = arguments[1];
108453             var pt$1 = arguments[2];
108454             this._component = component$1;
108455             this._segIndex = segIndex;
108456             this._pt = pt$1;
108457           }
108458         };
108459
108460         var staticAccessors$38 = { INSIDE_AREA: { configurable: true } };
108461         GeometryLocation.prototype.isInsideArea = function isInsideArea () {
108462           return this._segIndex === GeometryLocation.INSIDE_AREA
108463         };
108464         GeometryLocation.prototype.getCoordinate = function getCoordinate () {
108465           return this._pt
108466         };
108467         GeometryLocation.prototype.getGeometryComponent = function getGeometryComponent () {
108468           return this._component
108469         };
108470         GeometryLocation.prototype.getSegmentIndex = function getSegmentIndex () {
108471           return this._segIndex
108472         };
108473         GeometryLocation.prototype.interfaces_ = function interfaces_ () {
108474           return []
108475         };
108476         GeometryLocation.prototype.getClass = function getClass () {
108477           return GeometryLocation
108478         };
108479         staticAccessors$38.INSIDE_AREA.get = function () { return -1 };
108480
108481         Object.defineProperties( GeometryLocation, staticAccessors$38 );
108482
108483         var PointExtracter = function PointExtracter (pts) {
108484           this._pts = pts || null;
108485         };
108486         PointExtracter.prototype.filter = function filter (geom) {
108487           if (geom instanceof Point$1) { this._pts.add(geom); }
108488         };
108489         PointExtracter.prototype.interfaces_ = function interfaces_ () {
108490           return [GeometryFilter]
108491         };
108492         PointExtracter.prototype.getClass = function getClass () {
108493           return PointExtracter
108494         };
108495         PointExtracter.getPoints = function getPoints () {
108496           if (arguments.length === 1) {
108497             var geom = arguments[0];
108498             if (geom instanceof Point$1) {
108499               return Collections.singletonList(geom)
108500             }
108501             return PointExtracter.getPoints(geom, new ArrayList())
108502           } else if (arguments.length === 2) {
108503             var geom$1 = arguments[0];
108504             var list = arguments[1];
108505             if (geom$1 instanceof Point$1) {
108506               list.add(geom$1);
108507             } else if (geom$1 instanceof GeometryCollection) {
108508               geom$1.apply(new PointExtracter(list));
108509             }
108510             return list
108511           }
108512         };
108513
108514         var ConnectedElementLocationFilter = function ConnectedElementLocationFilter () {
108515           this._locations = null;
108516           var locations = arguments[0];
108517           this._locations = locations;
108518         };
108519         ConnectedElementLocationFilter.prototype.filter = function filter (geom) {
108520           if (geom instanceof Point$1 || geom instanceof LineString || geom instanceof Polygon) { this._locations.add(new GeometryLocation(geom, 0, geom.getCoordinate())); }
108521         };
108522         ConnectedElementLocationFilter.prototype.interfaces_ = function interfaces_ () {
108523           return [GeometryFilter]
108524         };
108525         ConnectedElementLocationFilter.prototype.getClass = function getClass () {
108526           return ConnectedElementLocationFilter
108527         };
108528         ConnectedElementLocationFilter.getLocations = function getLocations (geom) {
108529           var locations = new ArrayList();
108530           geom.apply(new ConnectedElementLocationFilter(locations));
108531           return locations
108532         };
108533
108534         var DistanceOp = function DistanceOp () {
108535           this._geom = null;
108536           this._terminateDistance = 0.0;
108537           this._ptLocator = new PointLocator();
108538           this._minDistanceLocation = null;
108539           this._minDistance = Double.MAX_VALUE;
108540           if (arguments.length === 2) {
108541             var g0 = arguments[0];
108542             var g1 = arguments[1];
108543             this._geom = [g0, g1];
108544             this._terminateDistance = 0.0;
108545           } else if (arguments.length === 3) {
108546             var g0$1 = arguments[0];
108547             var g1$1 = arguments[1];
108548             var terminateDistance = arguments[2];
108549             this._geom = new Array(2).fill(null);
108550             this._geom[0] = g0$1;
108551             this._geom[1] = g1$1;
108552             this._terminateDistance = terminateDistance;
108553           }
108554         };
108555         DistanceOp.prototype.computeContainmentDistance = function computeContainmentDistance () {
108556             var this$1 = this;
108557
108558           if (arguments.length === 0) {
108559             var locPtPoly = new Array(2).fill(null);
108560             this.computeContainmentDistance(0, locPtPoly);
108561             if (this._minDistance <= this._terminateDistance) { return null }
108562             this.computeContainmentDistance(1, locPtPoly);
108563           } else if (arguments.length === 2) {
108564             var polyGeomIndex = arguments[0];
108565             var locPtPoly$1 = arguments[1];
108566             var locationsIndex = 1 - polyGeomIndex;
108567             var polys = PolygonExtracter.getPolygons(this._geom[polyGeomIndex]);
108568             if (polys.size() > 0) {
108569               var insideLocs = ConnectedElementLocationFilter.getLocations(this._geom[locationsIndex]);
108570               this.computeContainmentDistance(insideLocs, polys, locPtPoly$1);
108571               if (this._minDistance <= this._terminateDistance) {
108572                 this._minDistanceLocation[locationsIndex] = locPtPoly$1[0];
108573                 this._minDistanceLocation[polyGeomIndex] = locPtPoly$1[1];
108574                 return null
108575               }
108576             }
108577           } else if (arguments.length === 3) {
108578             if (arguments[2] instanceof Array && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
108579               var locs = arguments[0];
108580               var polys$1 = arguments[1];
108581               var locPtPoly$2 = arguments[2];
108582               for (var i = 0; i < locs.size(); i++) {
108583                 var loc = locs.get(i);
108584                 for (var j = 0; j < polys$1.size(); j++) {
108585                   this$1.computeContainmentDistance(loc, polys$1.get(j), locPtPoly$2);
108586                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108587                 }
108588               }
108589             } else if (arguments[2] instanceof Array && (arguments[0] instanceof GeometryLocation && arguments[1] instanceof Polygon)) {
108590               var ptLoc = arguments[0];
108591               var poly = arguments[1];
108592               var locPtPoly$3 = arguments[2];
108593               var pt = ptLoc.getCoordinate();
108594               if (Location.EXTERIOR !== this._ptLocator.locate(pt, poly)) {
108595                 this._minDistance = 0.0;
108596                 locPtPoly$3[0] = ptLoc;
108597                 locPtPoly$3[1] = new GeometryLocation(poly, pt);
108598
108599                 return null
108600               }
108601             }
108602           }
108603         };
108604         DistanceOp.prototype.computeMinDistanceLinesPoints = function computeMinDistanceLinesPoints (lines, points, locGeom) {
108605             var this$1 = this;
108606
108607           for (var i = 0; i < lines.size(); i++) {
108608             var line = lines.get(i);
108609             for (var j = 0; j < points.size(); j++) {
108610               var pt = points.get(j);
108611               this$1.computeMinDistance(line, pt, locGeom);
108612               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108613             }
108614           }
108615         };
108616         DistanceOp.prototype.computeFacetDistance = function computeFacetDistance () {
108617           var locGeom = new Array(2).fill(null);
108618           var lines0 = LinearComponentExtracter.getLines(this._geom[0]);
108619           var lines1 = LinearComponentExtracter.getLines(this._geom[1]);
108620           var pts0 = PointExtracter.getPoints(this._geom[0]);
108621           var pts1 = PointExtracter.getPoints(this._geom[1]);
108622           this.computeMinDistanceLines(lines0, lines1, locGeom);
108623           this.updateMinDistance(locGeom, false);
108624           if (this._minDistance <= this._terminateDistance) { return null }
108625           locGeom[0] = null;
108626           locGeom[1] = null;
108627           this.computeMinDistanceLinesPoints(lines0, pts1, 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(lines1, pts0, locGeom);
108633           this.updateMinDistance(locGeom, true);
108634           if (this._minDistance <= this._terminateDistance) { return null }
108635           locGeom[0] = null;
108636           locGeom[1] = null;
108637           this.computeMinDistancePoints(pts0, pts1, locGeom);
108638           this.updateMinDistance(locGeom, false);
108639         };
108640         DistanceOp.prototype.nearestLocations = function nearestLocations () {
108641           this.computeMinDistance();
108642           return this._minDistanceLocation
108643         };
108644         DistanceOp.prototype.updateMinDistance = function updateMinDistance (locGeom, flip) {
108645           if (locGeom[0] === null) { return null }
108646           if (flip) {
108647             this._minDistanceLocation[0] = locGeom[1];
108648             this._minDistanceLocation[1] = locGeom[0];
108649           } else {
108650             this._minDistanceLocation[0] = locGeom[0];
108651             this._minDistanceLocation[1] = locGeom[1];
108652           }
108653         };
108654         DistanceOp.prototype.nearestPoints = function nearestPoints () {
108655           this.computeMinDistance();
108656           var nearestPts = [this._minDistanceLocation[0].getCoordinate(), this._minDistanceLocation[1].getCoordinate()];
108657           return nearestPts
108658         };
108659         DistanceOp.prototype.computeMinDistance = function computeMinDistance () {
108660             var this$1 = this;
108661
108662           if (arguments.length === 0) {
108663             if (this._minDistanceLocation !== null) { return null }
108664             this._minDistanceLocation = new Array(2).fill(null);
108665             this.computeContainmentDistance();
108666             if (this._minDistance <= this._terminateDistance) { return null }
108667             this.computeFacetDistance();
108668           } else if (arguments.length === 3) {
108669             if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof Point$1)) {
108670               var line = arguments[0];
108671               var pt = arguments[1];
108672               var locGeom = arguments[2];
108673               if (line.getEnvelopeInternal().distance(pt.getEnvelopeInternal()) > this._minDistance) { return null }
108674               var coord0 = line.getCoordinates();
108675               var coord = pt.getCoordinate();
108676               for (var i = 0; i < coord0.length - 1; i++) {
108677                 var dist = CGAlgorithms.distancePointLine(coord, coord0[i], coord0[i + 1]);
108678                 if (dist < this$1._minDistance) {
108679                   this$1._minDistance = dist;
108680                   var seg = new LineSegment(coord0[i], coord0[i + 1]);
108681                   var segClosestPoint = seg.closestPoint(coord);
108682                   locGeom[0] = new GeometryLocation(line, i, segClosestPoint);
108683                   locGeom[1] = new GeometryLocation(pt, 0, coord);
108684                 }
108685                 if (this$1._minDistance <= this$1._terminateDistance) { return null }
108686               }
108687             } else if (arguments[2] instanceof Array && (arguments[0] instanceof LineString && arguments[1] instanceof LineString)) {
108688               var line0 = arguments[0];
108689               var line1 = arguments[1];
108690               var locGeom$1 = arguments[2];
108691               if (line0.getEnvelopeInternal().distance(line1.getEnvelopeInternal()) > this._minDistance) { return null }
108692               var coord0$1 = line0.getCoordinates();
108693               var coord1 = line1.getCoordinates();
108694               for (var i$1 = 0; i$1 < coord0$1.length - 1; i$1++) {
108695                 for (var j = 0; j < coord1.length - 1; j++) {
108696                   var dist$1 = CGAlgorithms.distanceLineLine(coord0$1[i$1], coord0$1[i$1 + 1], coord1[j], coord1[j + 1]);
108697                   if (dist$1 < this$1._minDistance) {
108698                     this$1._minDistance = dist$1;
108699                     var seg0 = new LineSegment(coord0$1[i$1], coord0$1[i$1 + 1]);
108700                     var seg1 = new LineSegment(coord1[j], coord1[j + 1]);
108701                     var closestPt = seg0.closestPoints(seg1);
108702                     locGeom$1[0] = new GeometryLocation(line0, i$1, closestPt[0]);
108703                     locGeom$1[1] = new GeometryLocation(line1, j, closestPt[1]);
108704                   }
108705                   if (this$1._minDistance <= this$1._terminateDistance) { return null }
108706                 }
108707               }
108708             }
108709           }
108710         };
108711         DistanceOp.prototype.computeMinDistancePoints = function computeMinDistancePoints (points0, points1, locGeom) {
108712             var this$1 = this;
108713
108714           for (var i = 0; i < points0.size(); i++) {
108715             var pt0 = points0.get(i);
108716             for (var j = 0; j < points1.size(); j++) {
108717               var pt1 = points1.get(j);
108718               var dist = pt0.getCoordinate().distance(pt1.getCoordinate());
108719               if (dist < this$1._minDistance) {
108720                 this$1._minDistance = dist;
108721                 locGeom[0] = new GeometryLocation(pt0, 0, pt0.getCoordinate());
108722                 locGeom[1] = new GeometryLocation(pt1, 0, pt1.getCoordinate());
108723               }
108724               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108725             }
108726           }
108727         };
108728         DistanceOp.prototype.distance = function distance () {
108729           if (this._geom[0] === null || this._geom[1] === null) { throw new IllegalArgumentException('null geometries are not supported') }
108730           if (this._geom[0].isEmpty() || this._geom[1].isEmpty()) { return 0.0 }
108731           this.computeMinDistance();
108732           return this._minDistance
108733         };
108734         DistanceOp.prototype.computeMinDistanceLines = function computeMinDistanceLines (lines0, lines1, locGeom) {
108735             var this$1 = this;
108736
108737           for (var i = 0; i < lines0.size(); i++) {
108738             var line0 = lines0.get(i);
108739             for (var j = 0; j < lines1.size(); j++) {
108740               var line1 = lines1.get(j);
108741               this$1.computeMinDistance(line0, line1, locGeom);
108742               if (this$1._minDistance <= this$1._terminateDistance) { return null }
108743             }
108744           }
108745         };
108746         DistanceOp.prototype.interfaces_ = function interfaces_ () {
108747           return []
108748         };
108749         DistanceOp.prototype.getClass = function getClass () {
108750           return DistanceOp
108751         };
108752         DistanceOp.distance = function distance (g0, g1) {
108753           var distOp = new DistanceOp(g0, g1);
108754           return distOp.distance()
108755         };
108756         DistanceOp.isWithinDistance = function isWithinDistance (g0, g1, distance) {
108757           var distOp = new DistanceOp(g0, g1, distance);
108758           return distOp.distance() <= distance
108759         };
108760         DistanceOp.nearestPoints = function nearestPoints (g0, g1) {
108761           var distOp = new DistanceOp(g0, g1);
108762           return distOp.nearestPoints()
108763         };
108764
108765         var PointPairDistance$2 = function PointPairDistance () {
108766           this._pt = [new Coordinate(), new Coordinate()];
108767           this._distance = Double.NaN;
108768           this._isNull = true;
108769         };
108770         PointPairDistance$2.prototype.getCoordinates = function getCoordinates () {
108771           return this._pt
108772         };
108773         PointPairDistance$2.prototype.getCoordinate = function getCoordinate (i) {
108774           return this._pt[i]
108775         };
108776         PointPairDistance$2.prototype.setMinimum = function setMinimum () {
108777           if (arguments.length === 1) {
108778             var ptDist = arguments[0];
108779             this.setMinimum(ptDist._pt[0], ptDist._pt[1]);
108780           } else if (arguments.length === 2) {
108781             var p0 = arguments[0];
108782             var p1 = arguments[1];
108783             if (this._isNull) {
108784               this.initialize(p0, p1);
108785               return null
108786             }
108787             var dist = p0.distance(p1);
108788             if (dist < this._distance) { this.initialize(p0, p1, dist); }
108789           }
108790         };
108791         PointPairDistance$2.prototype.initialize = function initialize () {
108792           if (arguments.length === 0) {
108793             this._isNull = true;
108794           } else if (arguments.length === 2) {
108795             var p0 = arguments[0];
108796             var p1 = arguments[1];
108797             this._pt[0].setCoordinate(p0);
108798             this._pt[1].setCoordinate(p1);
108799             this._distance = p0.distance(p1);
108800             this._isNull = false;
108801           } else if (arguments.length === 3) {
108802             var p0$1 = arguments[0];
108803             var p1$1 = arguments[1];
108804             var distance = arguments[2];
108805             this._pt[0].setCoordinate(p0$1);
108806             this._pt[1].setCoordinate(p1$1);
108807             this._distance = distance;
108808             this._isNull = false;
108809           }
108810         };
108811         PointPairDistance$2.prototype.toString = function toString () {
108812           return WKTWriter.toLineString(this._pt[0], this._pt[1])
108813         };
108814         PointPairDistance$2.prototype.getDistance = function getDistance () {
108815           return this._distance
108816         };
108817         PointPairDistance$2.prototype.setMaximum = function setMaximum () {
108818           if (arguments.length === 1) {
108819             var ptDist = arguments[0];
108820             this.setMaximum(ptDist._pt[0], ptDist._pt[1]);
108821           } else if (arguments.length === 2) {
108822             var p0 = arguments[0];
108823             var p1 = arguments[1];
108824             if (this._isNull) {
108825               this.initialize(p0, p1);
108826               return null
108827             }
108828             var dist = p0.distance(p1);
108829             if (dist > this._distance) { this.initialize(p0, p1, dist); }
108830           }
108831         };
108832         PointPairDistance$2.prototype.interfaces_ = function interfaces_ () {
108833           return []
108834         };
108835         PointPairDistance$2.prototype.getClass = function getClass () {
108836           return PointPairDistance$2
108837         };
108838
108839         var DistanceToPoint = function DistanceToPoint () {};
108840
108841         DistanceToPoint.prototype.interfaces_ = function interfaces_ () {
108842           return []
108843         };
108844         DistanceToPoint.prototype.getClass = function getClass () {
108845           return DistanceToPoint
108846         };
108847         DistanceToPoint.computeDistance = function computeDistance () {
108848           if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineString && arguments[1] instanceof Coordinate)) {
108849             var line = arguments[0];
108850             var pt = arguments[1];
108851             var ptDist = arguments[2];
108852             var tempSegment = new LineSegment();
108853             var coords = line.getCoordinates();
108854             for (var i = 0; i < coords.length - 1; i++) {
108855               tempSegment.setCoordinates(coords[i], coords[i + 1]);
108856               var closestPt = tempSegment.closestPoint(pt);
108857               ptDist.setMinimum(closestPt, pt);
108858             }
108859           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Polygon && arguments[1] instanceof Coordinate)) {
108860             var poly = arguments[0];
108861             var pt$1 = arguments[1];
108862             var ptDist$1 = arguments[2];
108863             DistanceToPoint.computeDistance(poly.getExteriorRing(), pt$1, ptDist$1);
108864             for (var i$1 = 0; i$1 < poly.getNumInteriorRing(); i$1++) {
108865               DistanceToPoint.computeDistance(poly.getInteriorRingN(i$1), pt$1, ptDist$1);
108866             }
108867           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof Geometry && arguments[1] instanceof Coordinate)) {
108868             var geom = arguments[0];
108869             var pt$2 = arguments[1];
108870             var ptDist$2 = arguments[2];
108871             if (geom instanceof LineString) {
108872               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108873             } else if (geom instanceof Polygon) {
108874               DistanceToPoint.computeDistance(geom, pt$2, ptDist$2);
108875             } else if (geom instanceof GeometryCollection) {
108876               var gc = geom;
108877               for (var i$2 = 0; i$2 < gc.getNumGeometries(); i$2++) {
108878                 var g = gc.getGeometryN(i$2);
108879                 DistanceToPoint.computeDistance(g, pt$2, ptDist$2);
108880               }
108881             } else {
108882               ptDist$2.setMinimum(geom.getCoordinate(), pt$2);
108883             }
108884           } else if (arguments[2] instanceof PointPairDistance$2 && (arguments[0] instanceof LineSegment && arguments[1] instanceof Coordinate)) {
108885             var segment = arguments[0];
108886             var pt$3 = arguments[1];
108887             var ptDist$3 = arguments[2];
108888             var closestPt$1 = segment.closestPoint(pt$3);
108889             ptDist$3.setMinimum(closestPt$1, pt$3);
108890           }
108891         };
108892
108893         var DiscreteHausdorffDistance = function DiscreteHausdorffDistance () {
108894           this._g0 = null;
108895           this._g1 = null;
108896           this._ptDist = new PointPairDistance$2();
108897           this._densifyFrac = 0.0;
108898           var g0 = arguments[0];
108899           var g1 = arguments[1];
108900           this._g0 = g0;
108901           this._g1 = g1;
108902         };
108903
108904         var staticAccessors$39 = { MaxPointDistanceFilter: { configurable: true },MaxDensifiedByFractionDistanceFilter: { configurable: true } };
108905         DiscreteHausdorffDistance.prototype.getCoordinates = function getCoordinates () {
108906           return this._ptDist.getCoordinates()
108907         };
108908         DiscreteHausdorffDistance.prototype.setDensifyFraction = function setDensifyFraction (densifyFrac) {
108909           if (densifyFrac > 1.0 || densifyFrac <= 0.0) { throw new IllegalArgumentException('Fraction is not in range (0.0 - 1.0]') }
108910           this._densifyFrac = densifyFrac;
108911         };
108912         DiscreteHausdorffDistance.prototype.compute = function compute (g0, g1) {
108913           this.computeOrientedDistance(g0, g1, this._ptDist);
108914           this.computeOrientedDistance(g1, g0, this._ptDist);
108915         };
108916         DiscreteHausdorffDistance.prototype.distance = function distance () {
108917           this.compute(this._g0, this._g1);
108918           return this._ptDist.getDistance()
108919         };
108920         DiscreteHausdorffDistance.prototype.computeOrientedDistance = function computeOrientedDistance (discreteGeom, geom, ptDist) {
108921           var distFilter = new MaxPointDistanceFilter$1(geom);
108922           discreteGeom.apply(distFilter);
108923           ptDist.setMaximum(distFilter.getMaxPointDistance());
108924           if (this._densifyFrac > 0) {
108925             var fracFilter = new MaxDensifiedByFractionDistanceFilter(geom, this._densifyFrac);
108926             discreteGeom.apply(fracFilter);
108927             ptDist.setMaximum(fracFilter.getMaxPointDistance());
108928           }
108929         };
108930         DiscreteHausdorffDistance.prototype.orientedDistance = function orientedDistance () {
108931           this.computeOrientedDistance(this._g0, this._g1, this._ptDist);
108932           return this._ptDist.getDistance()
108933         };
108934         DiscreteHausdorffDistance.prototype.interfaces_ = function interfaces_ () {
108935           return []
108936         };
108937         DiscreteHausdorffDistance.prototype.getClass = function getClass () {
108938           return DiscreteHausdorffDistance
108939         };
108940         DiscreteHausdorffDistance.distance = function distance () {
108941           if (arguments.length === 2) {
108942             var g0 = arguments[0];
108943             var g1 = arguments[1];
108944             var dist = new DiscreteHausdorffDistance(g0, g1);
108945             return dist.distance()
108946           } else if (arguments.length === 3) {
108947             var g0$1 = arguments[0];
108948             var g1$1 = arguments[1];
108949             var densifyFrac = arguments[2];
108950             var dist$1 = new DiscreteHausdorffDistance(g0$1, g1$1);
108951             dist$1.setDensifyFraction(densifyFrac);
108952             return dist$1.distance()
108953           }
108954         };
108955         staticAccessors$39.MaxPointDistanceFilter.get = function () { return MaxPointDistanceFilter$1 };
108956         staticAccessors$39.MaxDensifiedByFractionDistanceFilter.get = function () { return MaxDensifiedByFractionDistanceFilter };
108957
108958         Object.defineProperties( DiscreteHausdorffDistance, staticAccessors$39 );
108959
108960         var MaxPointDistanceFilter$1 = function MaxPointDistanceFilter () {
108961           this._maxPtDist = new PointPairDistance$2();
108962           this._minPtDist = new PointPairDistance$2();
108963           this._euclideanDist = new DistanceToPoint();
108964           this._geom = null;
108965           var geom = arguments[0];
108966           this._geom = geom;
108967         };
108968         MaxPointDistanceFilter$1.prototype.filter = function filter (pt) {
108969           this._minPtDist.initialize();
108970           DistanceToPoint.computeDistance(this._geom, pt, this._minPtDist);
108971           this._maxPtDist.setMaximum(this._minPtDist);
108972         };
108973         MaxPointDistanceFilter$1.prototype.getMaxPointDistance = function getMaxPointDistance () {
108974           return this._maxPtDist
108975         };
108976         MaxPointDistanceFilter$1.prototype.interfaces_ = function interfaces_ () {
108977           return [CoordinateFilter]
108978         };
108979         MaxPointDistanceFilter$1.prototype.getClass = function getClass () {
108980           return MaxPointDistanceFilter$1
108981         };
108982
108983         var MaxDensifiedByFractionDistanceFilter = function MaxDensifiedByFractionDistanceFilter () {
108984           this._maxPtDist = new PointPairDistance$2();
108985           this._minPtDist = new PointPairDistance$2();
108986           this._geom = null;
108987           this._numSubSegs = 0;
108988           var geom = arguments[0];
108989           var fraction = arguments[1];
108990           this._geom = geom;
108991           this._numSubSegs = Math.trunc(Math.round(1.0 / fraction));
108992         };
108993         MaxDensifiedByFractionDistanceFilter.prototype.filter = function filter (seq, index) {
108994             var this$1 = this;
108995
108996           if (index === 0) { return null }
108997           var p0 = seq.getCoordinate(index - 1);
108998           var p1 = seq.getCoordinate(index);
108999           var delx = (p1.x - p0.x) / this._numSubSegs;
109000           var dely = (p1.y - p0.y) / this._numSubSegs;
109001           for (var i = 0; i < this._numSubSegs; i++) {
109002             var x = p0.x + i * delx;
109003             var y = p0.y + i * dely;
109004             var pt = new Coordinate(x, y);
109005             this$1._minPtDist.initialize();
109006             DistanceToPoint.computeDistance(this$1._geom, pt, this$1._minPtDist);
109007             this$1._maxPtDist.setMaximum(this$1._minPtDist);
109008           }
109009         };
109010         MaxDensifiedByFractionDistanceFilter.prototype.isDone = function isDone () {
109011           return false
109012         };
109013         MaxDensifiedByFractionDistanceFilter.prototype.isGeometryChanged = function isGeometryChanged () {
109014           return false
109015         };
109016         MaxDensifiedByFractionDistanceFilter.prototype.getMaxPointDistance = function getMaxPointDistance () {
109017           return this._maxPtDist
109018         };
109019         MaxDensifiedByFractionDistanceFilter.prototype.interfaces_ = function interfaces_ () {
109020           return [CoordinateSequenceFilter]
109021         };
109022         MaxDensifiedByFractionDistanceFilter.prototype.getClass = function getClass () {
109023           return MaxDensifiedByFractionDistanceFilter
109024         };
109025
109026         var BufferDistanceValidator = function BufferDistanceValidator (input, bufDistance, result) {
109027           this._minValidDistance = null;
109028           this._maxValidDistance = null;
109029           this._minDistanceFound = null;
109030           this._maxDistanceFound = null;
109031           this._isValid = true;
109032           this._errMsg = null;
109033           this._errorLocation = null;
109034           this._errorIndicator = null;
109035           this._input = input || null;
109036           this._bufDistance = bufDistance || null;
109037           this._result = result || null;
109038         };
109039
109040         var staticAccessors$37 = { VERBOSE: { configurable: true },MAX_DISTANCE_DIFF_FRAC: { configurable: true } };
109041         BufferDistanceValidator.prototype.checkMaximumDistance = function checkMaximumDistance (input, bufCurve, maxDist) {
109042           var haus = new DiscreteHausdorffDistance(bufCurve, input);
109043           haus.setDensifyFraction(0.25);
109044           this._maxDistanceFound = haus.orientedDistance();
109045           if (this._maxDistanceFound > maxDist) {
109046             this._isValid = false;
109047             var pts = haus.getCoordinates();
109048             this._errorLocation = pts[1];
109049             this._errorIndicator = input.getFactory().createLineString(pts);
109050             this._errMsg = 'Distance between buffer curve and input is too large (' + this._maxDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ')';
109051           }
109052         };
109053         BufferDistanceValidator.prototype.isValid = function isValid () {
109054           var posDistance = Math.abs(this._bufDistance);
109055           var distDelta = BufferDistanceValidator.MAX_DISTANCE_DIFF_FRAC * posDistance;
109056           this._minValidDistance = posDistance - distDelta;
109057           this._maxValidDistance = posDistance + distDelta;
109058           if (this._input.isEmpty() || this._result.isEmpty()) { return true }
109059           if (this._bufDistance > 0.0) {
109060             this.checkPositiveValid();
109061           } else {
109062             this.checkNegativeValid();
109063           }
109064           if (BufferDistanceValidator.VERBOSE) {
109065             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));
109066           }
109067           return this._isValid
109068         };
109069         BufferDistanceValidator.prototype.checkNegativeValid = function checkNegativeValid () {
109070           if (!(this._input instanceof Polygon || this._input instanceof MultiPolygon || this._input instanceof GeometryCollection)) {
109071             return null
109072           }
109073           var inputCurve = this.getPolygonLines(this._input);
109074           this.checkMinimumDistance(inputCurve, this._result, this._minValidDistance);
109075           if (!this._isValid) { return null }
109076           this.checkMaximumDistance(inputCurve, this._result, this._maxValidDistance);
109077         };
109078         BufferDistanceValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109079           return this._errorIndicator
109080         };
109081         BufferDistanceValidator.prototype.checkMinimumDistance = function checkMinimumDistance (g1, g2, minDist) {
109082           var distOp = new DistanceOp(g1, g2, minDist);
109083           this._minDistanceFound = distOp.distance();
109084           if (this._minDistanceFound < minDist) {
109085             this._isValid = false;
109086             var pts = distOp.nearestPoints();
109087             this._errorLocation = distOp.nearestPoints()[1];
109088             this._errorIndicator = g1.getFactory().createLineString(pts);
109089             this._errMsg = 'Distance between buffer curve and input is too small (' + this._minDistanceFound + ' at ' + WKTWriter.toLineString(pts[0], pts[1]) + ' )';
109090           }
109091         };
109092         BufferDistanceValidator.prototype.checkPositiveValid = function checkPositiveValid () {
109093           var bufCurve = this._result.getBoundary();
109094           this.checkMinimumDistance(this._input, bufCurve, this._minValidDistance);
109095           if (!this._isValid) { return null }
109096           this.checkMaximumDistance(this._input, bufCurve, this._maxValidDistance);
109097         };
109098         BufferDistanceValidator.prototype.getErrorLocation = function getErrorLocation () {
109099           return this._errorLocation
109100         };
109101         BufferDistanceValidator.prototype.getPolygonLines = function getPolygonLines (g) {
109102           var lines = new ArrayList();
109103           var lineExtracter = new LinearComponentExtracter(lines);
109104           var polys = PolygonExtracter.getPolygons(g);
109105           for (var i = polys.iterator(); i.hasNext();) {
109106             var poly = i.next();
109107             poly.apply(lineExtracter);
109108           }
109109           return g.getFactory().buildGeometry(lines)
109110         };
109111         BufferDistanceValidator.prototype.getErrorMessage = function getErrorMessage () {
109112           return this._errMsg
109113         };
109114         BufferDistanceValidator.prototype.interfaces_ = function interfaces_ () {
109115           return []
109116         };
109117         BufferDistanceValidator.prototype.getClass = function getClass () {
109118           return BufferDistanceValidator
109119         };
109120         staticAccessors$37.VERBOSE.get = function () { return false };
109121         staticAccessors$37.MAX_DISTANCE_DIFF_FRAC.get = function () { return 0.012 };
109122
109123         Object.defineProperties( BufferDistanceValidator, staticAccessors$37 );
109124
109125         var BufferResultValidator = function BufferResultValidator (input, distance, result) {
109126           this._isValid = true;
109127           this._errorMsg = null;
109128           this._errorLocation = null;
109129           this._errorIndicator = null;
109130           this._input = input || null;
109131           this._distance = distance || null;
109132           this._result = result || null;
109133         };
109134
109135         var staticAccessors$40 = { VERBOSE: { configurable: true },MAX_ENV_DIFF_FRAC: { configurable: true } };
109136         BufferResultValidator.prototype.isValid = function isValid () {
109137           this.checkPolygonal();
109138           if (!this._isValid) { return this._isValid }
109139           this.checkExpectedEmpty();
109140           if (!this._isValid) { return this._isValid }
109141           this.checkEnvelope();
109142           if (!this._isValid) { return this._isValid }
109143           this.checkArea();
109144           if (!this._isValid) { return this._isValid }
109145           this.checkDistance();
109146           return this._isValid
109147         };
109148         BufferResultValidator.prototype.checkEnvelope = function checkEnvelope () {
109149           if (this._distance < 0.0) { return null }
109150           var padding = this._distance * BufferResultValidator.MAX_ENV_DIFF_FRAC;
109151           if (padding === 0.0) { padding = 0.001; }
109152           var expectedEnv = new Envelope(this._input.getEnvelopeInternal());
109153           expectedEnv.expandBy(this._distance);
109154           var bufEnv = new Envelope(this._result.getEnvelopeInternal());
109155           bufEnv.expandBy(padding);
109156           if (!bufEnv.contains(expectedEnv)) {
109157             this._isValid = false;
109158             this._errorMsg = 'Buffer envelope is incorrect';
109159             this._errorIndicator = this._input.getFactory().toGeometry(bufEnv);
109160           }
109161           this.report('Envelope');
109162         };
109163         BufferResultValidator.prototype.checkDistance = function checkDistance () {
109164           var distValid = new BufferDistanceValidator(this._input, this._distance, this._result);
109165           if (!distValid.isValid()) {
109166             this._isValid = false;
109167             this._errorMsg = distValid.getErrorMessage();
109168             this._errorLocation = distValid.getErrorLocation();
109169             this._errorIndicator = distValid.getErrorIndicator();
109170           }
109171           this.report('Distance');
109172         };
109173         BufferResultValidator.prototype.checkArea = function checkArea () {
109174           var inputArea = this._input.getArea();
109175           var resultArea = this._result.getArea();
109176           if (this._distance > 0.0 && inputArea > resultArea) {
109177             this._isValid = false;
109178             this._errorMsg = 'Area of positive buffer is smaller than input';
109179             this._errorIndicator = this._result;
109180           }
109181           if (this._distance < 0.0 && inputArea < resultArea) {
109182             this._isValid = false;
109183             this._errorMsg = 'Area of negative buffer is larger than input';
109184             this._errorIndicator = this._result;
109185           }
109186           this.report('Area');
109187         };
109188         BufferResultValidator.prototype.checkPolygonal = function checkPolygonal () {
109189           if (!(this._result instanceof Polygon || this._result instanceof MultiPolygon)) { this._isValid = false; }
109190           this._errorMsg = 'Result is not polygonal';
109191           this._errorIndicator = this._result;
109192           this.report('Polygonal');
109193         };
109194         BufferResultValidator.prototype.getErrorIndicator = function getErrorIndicator () {
109195           return this._errorIndicator
109196         };
109197         BufferResultValidator.prototype.getErrorLocation = function getErrorLocation () {
109198           return this._errorLocation
109199         };
109200         BufferResultValidator.prototype.checkExpectedEmpty = function checkExpectedEmpty () {
109201           if (this._input.getDimension() >= 2) { return null }
109202           if (this._distance > 0.0) { return null }
109203           if (!this._result.isEmpty()) {
109204             this._isValid = false;
109205             this._errorMsg = 'Result is non-empty';
109206             this._errorIndicator = this._result;
109207           }
109208           this.report('ExpectedEmpty');
109209         };
109210         BufferResultValidator.prototype.report = function report (checkName) {
109211           if (!BufferResultValidator.VERBOSE) { return null }
109212           System.out.println('Check ' + checkName + ': ' + (this._isValid ? 'passed' : 'FAILED'));
109213         };
109214         BufferResultValidator.prototype.getErrorMessage = function getErrorMessage () {
109215           return this._errorMsg
109216         };
109217         BufferResultValidator.prototype.interfaces_ = function interfaces_ () {
109218           return []
109219         };
109220         BufferResultValidator.prototype.getClass = function getClass () {
109221           return BufferResultValidator
109222         };
109223         BufferResultValidator.isValidMsg = function isValidMsg (g, distance, result) {
109224           var validator = new BufferResultValidator(g, distance, result);
109225           if (!validator.isValid()) { return validator.getErrorMessage() }
109226           return null
109227         };
109228         BufferResultValidator.isValid = function isValid (g, distance, result) {
109229           var validator = new BufferResultValidator(g, distance, result);
109230           if (validator.isValid()) { return true }
109231           return false
109232         };
109233         staticAccessors$40.VERBOSE.get = function () { return false };
109234         staticAccessors$40.MAX_ENV_DIFF_FRAC.get = function () { return 0.012 };
109235
109236         Object.defineProperties( BufferResultValidator, staticAccessors$40 );
109237
109238         // operation.buffer
109239
109240         var BasicSegmentString = function BasicSegmentString () {
109241           this._pts = null;
109242           this._data = null;
109243           var pts = arguments[0];
109244           var data = arguments[1];
109245           this._pts = pts;
109246           this._data = data;
109247         };
109248         BasicSegmentString.prototype.getCoordinates = function getCoordinates () {
109249           return this._pts
109250         };
109251         BasicSegmentString.prototype.size = function size () {
109252           return this._pts.length
109253         };
109254         BasicSegmentString.prototype.getCoordinate = function getCoordinate (i) {
109255           return this._pts[i]
109256         };
109257         BasicSegmentString.prototype.isClosed = function isClosed () {
109258           return this._pts[0].equals(this._pts[this._pts.length - 1])
109259         };
109260         BasicSegmentString.prototype.getSegmentOctant = function getSegmentOctant (index) {
109261           if (index === this._pts.length - 1) { return -1 }
109262           return Octant.octant(this.getCoordinate(index), this.getCoordinate(index + 1))
109263         };
109264         BasicSegmentString.prototype.setData = function setData (data) {
109265           this._data = data;
109266         };
109267         BasicSegmentString.prototype.getData = function getData () {
109268           return this._data
109269         };
109270         BasicSegmentString.prototype.toString = function toString () {
109271           return WKTWriter.toLineString(new CoordinateArraySequence(this._pts))
109272         };
109273         BasicSegmentString.prototype.interfaces_ = function interfaces_ () {
109274           return [SegmentString]
109275         };
109276         BasicSegmentString.prototype.getClass = function getClass () {
109277           return BasicSegmentString
109278         };
109279
109280         var InteriorIntersectionFinder = function InteriorIntersectionFinder () {
109281           this._findAllIntersections = false;
109282           this._isCheckEndSegmentsOnly = false;
109283           this._li = null;
109284           this._interiorIntersection = null;
109285           this._intSegments = null;
109286           this._intersections = new ArrayList();
109287           this._intersectionCount = 0;
109288           this._keepIntersections = true;
109289           var li = arguments[0];
109290           this._li = li;
109291           this._interiorIntersection = null;
109292         };
109293         InteriorIntersectionFinder.prototype.getInteriorIntersection = function getInteriorIntersection () {
109294           return this._interiorIntersection
109295         };
109296         InteriorIntersectionFinder.prototype.setCheckEndSegmentsOnly = function setCheckEndSegmentsOnly (isCheckEndSegmentsOnly) {
109297           this._isCheckEndSegmentsOnly = isCheckEndSegmentsOnly;
109298         };
109299         InteriorIntersectionFinder.prototype.getIntersectionSegments = function getIntersectionSegments () {
109300           return this._intSegments
109301         };
109302         InteriorIntersectionFinder.prototype.count = function count () {
109303           return this._intersectionCount
109304         };
109305         InteriorIntersectionFinder.prototype.getIntersections = function getIntersections () {
109306           return this._intersections
109307         };
109308         InteriorIntersectionFinder.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109309           this._findAllIntersections = findAllIntersections;
109310         };
109311         InteriorIntersectionFinder.prototype.setKeepIntersections = function setKeepIntersections (keepIntersections) {
109312           this._keepIntersections = keepIntersections;
109313         };
109314         InteriorIntersectionFinder.prototype.processIntersections = function processIntersections (e0, segIndex0, e1, segIndex1) {
109315           if (!this._findAllIntersections && this.hasIntersection()) { return null }
109316           if (e0 === e1 && segIndex0 === segIndex1) { return null }
109317           if (this._isCheckEndSegmentsOnly) {
109318             var isEndSegPresent = this.isEndSegment(e0, segIndex0) || this.isEndSegment(e1, segIndex1);
109319             if (!isEndSegPresent) { return null }
109320           }
109321           var p00 = e0.getCoordinates()[segIndex0];
109322           var p01 = e0.getCoordinates()[segIndex0 + 1];
109323           var p10 = e1.getCoordinates()[segIndex1];
109324           var p11 = e1.getCoordinates()[segIndex1 + 1];
109325           this._li.computeIntersection(p00, p01, p10, p11);
109326           if (this._li.hasIntersection()) {
109327             if (this._li.isInteriorIntersection()) {
109328               this._intSegments = new Array(4).fill(null);
109329               this._intSegments[0] = p00;
109330               this._intSegments[1] = p01;
109331               this._intSegments[2] = p10;
109332               this._intSegments[3] = p11;
109333               this._interiorIntersection = this._li.getIntersection(0);
109334               if (this._keepIntersections) { this._intersections.add(this._interiorIntersection); }
109335               this._intersectionCount++;
109336             }
109337           }
109338         };
109339         InteriorIntersectionFinder.prototype.isEndSegment = function isEndSegment (segStr, index) {
109340           if (index === 0) { return true }
109341           if (index >= segStr.size() - 2) { return true }
109342           return false
109343         };
109344         InteriorIntersectionFinder.prototype.hasIntersection = function hasIntersection () {
109345           return this._interiorIntersection !== null
109346         };
109347         InteriorIntersectionFinder.prototype.isDone = function isDone () {
109348           if (this._findAllIntersections) { return false }
109349           return this._interiorIntersection !== null
109350         };
109351         InteriorIntersectionFinder.prototype.interfaces_ = function interfaces_ () {
109352           return [SegmentIntersector]
109353         };
109354         InteriorIntersectionFinder.prototype.getClass = function getClass () {
109355           return InteriorIntersectionFinder
109356         };
109357         InteriorIntersectionFinder.createAllIntersectionsFinder = function createAllIntersectionsFinder (li) {
109358           var finder = new InteriorIntersectionFinder(li);
109359           finder.setFindAllIntersections(true);
109360           return finder
109361         };
109362         InteriorIntersectionFinder.createAnyIntersectionFinder = function createAnyIntersectionFinder (li) {
109363           return new InteriorIntersectionFinder(li)
109364         };
109365         InteriorIntersectionFinder.createIntersectionCounter = function createIntersectionCounter (li) {
109366           var finder = new InteriorIntersectionFinder(li);
109367           finder.setFindAllIntersections(true);
109368           finder.setKeepIntersections(false);
109369           return finder
109370         };
109371
109372         var FastNodingValidator = function FastNodingValidator () {
109373           this._li = new RobustLineIntersector();
109374           this._segStrings = null;
109375           this._findAllIntersections = false;
109376           this._segInt = null;
109377           this._isValid = true;
109378           var segStrings = arguments[0];
109379           this._segStrings = segStrings;
109380         };
109381         FastNodingValidator.prototype.execute = function execute () {
109382           if (this._segInt !== null) { return null }
109383           this.checkInteriorIntersections();
109384         };
109385         FastNodingValidator.prototype.getIntersections = function getIntersections () {
109386           return this._segInt.getIntersections()
109387         };
109388         FastNodingValidator.prototype.isValid = function isValid () {
109389           this.execute();
109390           return this._isValid
109391         };
109392         FastNodingValidator.prototype.setFindAllIntersections = function setFindAllIntersections (findAllIntersections) {
109393           this._findAllIntersections = findAllIntersections;
109394         };
109395         FastNodingValidator.prototype.checkInteriorIntersections = function checkInteriorIntersections () {
109396           this._isValid = true;
109397           this._segInt = new InteriorIntersectionFinder(this._li);
109398           this._segInt.setFindAllIntersections(this._findAllIntersections);
109399           var noder = new MCIndexNoder();
109400           noder.setSegmentIntersector(this._segInt);
109401           noder.computeNodes(this._segStrings);
109402           if (this._segInt.hasIntersection()) {
109403             this._isValid = false;
109404             return null
109405           }
109406         };
109407         FastNodingValidator.prototype.checkValid = function checkValid () {
109408           this.execute();
109409           if (!this._isValid) { throw new TopologyException(this.getErrorMessage(), this._segInt.getInteriorIntersection()) }
109410         };
109411         FastNodingValidator.prototype.getErrorMessage = function getErrorMessage () {
109412           if (this._isValid) { return 'no intersections found' }
109413           var intSegs = this._segInt.getIntersectionSegments();
109414           return 'found non-noded intersection between ' + WKTWriter.toLineString(intSegs[0], intSegs[1]) + ' and ' + WKTWriter.toLineString(intSegs[2], intSegs[3])
109415         };
109416         FastNodingValidator.prototype.interfaces_ = function interfaces_ () {
109417           return []
109418         };
109419         FastNodingValidator.prototype.getClass = function getClass () {
109420           return FastNodingValidator
109421         };
109422         FastNodingValidator.computeIntersections = function computeIntersections (segStrings) {
109423           var nv = new FastNodingValidator(segStrings);
109424           nv.setFindAllIntersections(true);
109425           nv.isValid();
109426           return nv.getIntersections()
109427         };
109428
109429         var EdgeNodingValidator = function EdgeNodingValidator () {
109430           this._nv = null;
109431           var edges = arguments[0];
109432           this._nv = new FastNodingValidator(EdgeNodingValidator.toSegmentStrings(edges));
109433         };
109434         EdgeNodingValidator.prototype.checkValid = function checkValid () {
109435           this._nv.checkValid();
109436         };
109437         EdgeNodingValidator.prototype.interfaces_ = function interfaces_ () {
109438           return []
109439         };
109440         EdgeNodingValidator.prototype.getClass = function getClass () {
109441           return EdgeNodingValidator
109442         };
109443         EdgeNodingValidator.toSegmentStrings = function toSegmentStrings (edges) {
109444           var segStrings = new ArrayList();
109445           for (var i = edges.iterator(); i.hasNext();) {
109446             var e = i.next();
109447             segStrings.add(new BasicSegmentString(e.getCoordinates(), e));
109448           }
109449           return segStrings
109450         };
109451         EdgeNodingValidator.checkValid = function checkValid (edges) {
109452           var validator = new EdgeNodingValidator(edges);
109453           validator.checkValid();
109454         };
109455
109456         var GeometryCollectionMapper = function GeometryCollectionMapper (mapOp) {
109457           this._mapOp = mapOp;
109458         };
109459         GeometryCollectionMapper.prototype.map = function map (gc) {
109460             var this$1 = this;
109461
109462           var mapped = new ArrayList();
109463           for (var i = 0; i < gc.getNumGeometries(); i++) {
109464             var g = this$1._mapOp.map(gc.getGeometryN(i));
109465             if (!g.isEmpty()) { mapped.add(g); }
109466           }
109467           return gc.getFactory().createGeometryCollection(GeometryFactory.toGeometryArray(mapped))
109468         };
109469         GeometryCollectionMapper.prototype.interfaces_ = function interfaces_ () {
109470           return []
109471         };
109472         GeometryCollectionMapper.prototype.getClass = function getClass () {
109473           return GeometryCollectionMapper
109474         };
109475         GeometryCollectionMapper.map = function map (gc, op) {
109476           var mapper = new GeometryCollectionMapper(op);
109477           return mapper.map(gc)
109478         };
109479
109480         var LineBuilder = function LineBuilder () {
109481           this._op = null;
109482           this._geometryFactory = null;
109483           this._ptLocator = null;
109484           this._lineEdgesList = new ArrayList();
109485           this._resultLineList = new ArrayList();
109486           var op = arguments[0];
109487           var geometryFactory = arguments[1];
109488           var ptLocator = arguments[2];
109489           this._op = op;
109490           this._geometryFactory = geometryFactory;
109491           this._ptLocator = ptLocator;
109492         };
109493         LineBuilder.prototype.collectLines = function collectLines (opCode) {
109494             var this$1 = this;
109495
109496           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109497             var de = it.next();
109498             this$1.collectLineEdge(de, opCode, this$1._lineEdgesList);
109499             this$1.collectBoundaryTouchEdge(de, opCode, this$1._lineEdgesList);
109500           }
109501         };
109502         LineBuilder.prototype.labelIsolatedLine = function labelIsolatedLine (e, targetIndex) {
109503           var loc = this._ptLocator.locate(e.getCoordinate(), this._op.getArgGeometry(targetIndex));
109504           e.getLabel().setLocation(targetIndex, loc);
109505         };
109506         LineBuilder.prototype.build = function build (opCode) {
109507           this.findCoveredLineEdges();
109508           this.collectLines(opCode);
109509           this.buildLines(opCode);
109510           return this._resultLineList
109511         };
109512         LineBuilder.prototype.collectLineEdge = function collectLineEdge (de, opCode, edges) {
109513           var label = de.getLabel();
109514           var e = de.getEdge();
109515           if (de.isLineEdge()) {
109516             if (!de.isVisited() && OverlayOp.isResultOfOp(label, opCode) && !e.isCovered()) {
109517               edges.add(e);
109518               de.setVisitedEdge(true);
109519             }
109520           }
109521         };
109522         LineBuilder.prototype.findCoveredLineEdges = function findCoveredLineEdges () {
109523             var this$1 = this;
109524
109525           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109526             var node = nodeit.next();
109527             node.getEdges().findCoveredLineEdges();
109528           }
109529           for (var it = this._op.getGraph().getEdgeEnds().iterator(); it.hasNext();) {
109530             var de = it.next();
109531             var e = de.getEdge();
109532             if (de.isLineEdge() && !e.isCoveredSet()) {
109533               var isCovered = this$1._op.isCoveredByA(de.getCoordinate());
109534               e.setCovered(isCovered);
109535             }
109536           }
109537         };
109538         LineBuilder.prototype.labelIsolatedLines = function labelIsolatedLines (edgesList) {
109539             var this$1 = this;
109540
109541           for (var it = edgesList.iterator(); it.hasNext();) {
109542             var e = it.next();
109543             var label = e.getLabel();
109544             if (e.isIsolated()) {
109545               if (label.isNull(0)) { this$1.labelIsolatedLine(e, 0); } else { this$1.labelIsolatedLine(e, 1); }
109546             }
109547           }
109548         };
109549         LineBuilder.prototype.buildLines = function buildLines (opCode) {
109550             var this$1 = this;
109551
109552           for (var it = this._lineEdgesList.iterator(); it.hasNext();) {
109553             var e = it.next();
109554             // const label = e.getLabel()
109555             var line = this$1._geometryFactory.createLineString(e.getCoordinates());
109556             this$1._resultLineList.add(line);
109557             e.setInResult(true);
109558           }
109559         };
109560         LineBuilder.prototype.collectBoundaryTouchEdge = function collectBoundaryTouchEdge (de, opCode, edges) {
109561           var label = de.getLabel();
109562           if (de.isLineEdge()) { return null }
109563           if (de.isVisited()) { return null }
109564           if (de.isInteriorAreaEdge()) { return null }
109565           if (de.getEdge().isInResult()) { return null }
109566           Assert.isTrue(!(de.isInResult() || de.getSym().isInResult()) || !de.getEdge().isInResult());
109567           if (OverlayOp.isResultOfOp(label, opCode) && opCode === OverlayOp.INTERSECTION) {
109568             edges.add(de.getEdge());
109569             de.setVisitedEdge(true);
109570           }
109571         };
109572         LineBuilder.prototype.interfaces_ = function interfaces_ () {
109573           return []
109574         };
109575         LineBuilder.prototype.getClass = function getClass () {
109576           return LineBuilder
109577         };
109578
109579         var PointBuilder = function PointBuilder () {
109580           this._op = null;
109581           this._geometryFactory = null;
109582           this._resultPointList = new ArrayList();
109583           var op = arguments[0];
109584           var geometryFactory = arguments[1];
109585           // const ptLocator = arguments[2]
109586           this._op = op;
109587           this._geometryFactory = geometryFactory;
109588         };
109589         PointBuilder.prototype.filterCoveredNodeToPoint = function filterCoveredNodeToPoint (n) {
109590           var coord = n.getCoordinate();
109591           if (!this._op.isCoveredByLA(coord)) {
109592             var pt = this._geometryFactory.createPoint(coord);
109593             this._resultPointList.add(pt);
109594           }
109595         };
109596         PointBuilder.prototype.extractNonCoveredResultNodes = function extractNonCoveredResultNodes (opCode) {
109597             var this$1 = this;
109598
109599           for (var nodeit = this._op.getGraph().getNodes().iterator(); nodeit.hasNext();) {
109600             var n = nodeit.next();
109601             if (n.isInResult()) { continue }
109602             if (n.isIncidentEdgeInResult()) { continue }
109603             if (n.getEdges().getDegree() === 0 || opCode === OverlayOp.INTERSECTION) {
109604               var label = n.getLabel();
109605               if (OverlayOp.isResultOfOp(label, opCode)) {
109606                 this$1.filterCoveredNodeToPoint(n);
109607               }
109608             }
109609           }
109610         };
109611         PointBuilder.prototype.build = function build (opCode) {
109612           this.extractNonCoveredResultNodes(opCode);
109613           return this._resultPointList
109614         };
109615         PointBuilder.prototype.interfaces_ = function interfaces_ () {
109616           return []
109617         };
109618         PointBuilder.prototype.getClass = function getClass () {
109619           return PointBuilder
109620         };
109621
109622         var GeometryTransformer = function GeometryTransformer () {
109623           this._inputGeom = null;
109624           this._factory = null;
109625           this._pruneEmptyGeometry = true;
109626           this._preserveGeometryCollectionType = true;
109627           this._preserveCollections = false;
109628           this._preserveType = false;
109629         };
109630         GeometryTransformer.prototype.transformPoint = function transformPoint (geom, parent) {
109631           return this._factory.createPoint(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109632         };
109633         GeometryTransformer.prototype.transformPolygon = function transformPolygon (geom, parent) {
109634             var this$1 = this;
109635
109636           var isAllValidLinearRings = true;
109637           var shell = this.transformLinearRing(geom.getExteriorRing(), geom);
109638           if (shell === null || !(shell instanceof LinearRing) || shell.isEmpty()) { isAllValidLinearRings = false; }
109639           var holes = new ArrayList();
109640           for (var i = 0; i < geom.getNumInteriorRing(); i++) {
109641             var hole = this$1.transformLinearRing(geom.getInteriorRingN(i), geom);
109642             if (hole === null || hole.isEmpty()) {
109643               continue
109644             }
109645             if (!(hole instanceof LinearRing)) { isAllValidLinearRings = false; }
109646             holes.add(hole);
109647           }
109648           if (isAllValidLinearRings) { return this._factory.createPolygon(shell, holes.toArray([])); } else {
109649             var components = new ArrayList();
109650             if (shell !== null) { components.add(shell); }
109651             components.addAll(holes);
109652             return this._factory.buildGeometry(components)
109653           }
109654         };
109655         GeometryTransformer.prototype.createCoordinateSequence = function createCoordinateSequence (coords) {
109656           return this._factory.getCoordinateSequenceFactory().create(coords)
109657         };
109658         GeometryTransformer.prototype.getInputGeometry = function getInputGeometry () {
109659           return this._inputGeom
109660         };
109661         GeometryTransformer.prototype.transformMultiLineString = function transformMultiLineString (geom, parent) {
109662             var this$1 = this;
109663
109664           var transGeomList = new ArrayList();
109665           for (var i = 0; i < geom.getNumGeometries(); i++) {
109666             var transformGeom = this$1.transformLineString(geom.getGeometryN(i), geom);
109667             if (transformGeom === null) { continue }
109668             if (transformGeom.isEmpty()) { continue }
109669             transGeomList.add(transformGeom);
109670           }
109671           return this._factory.buildGeometry(transGeomList)
109672         };
109673         GeometryTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109674           return this.copy(coords)
109675         };
109676         GeometryTransformer.prototype.transformLineString = function transformLineString (geom, parent) {
109677           return this._factory.createLineString(this.transformCoordinates(geom.getCoordinateSequence(), geom))
109678         };
109679         GeometryTransformer.prototype.transformMultiPoint = function transformMultiPoint (geom, parent) {
109680             var this$1 = this;
109681
109682           var transGeomList = new ArrayList();
109683           for (var i = 0; i < geom.getNumGeometries(); i++) {
109684             var transformGeom = this$1.transformPoint(geom.getGeometryN(i), geom);
109685             if (transformGeom === null) { continue }
109686             if (transformGeom.isEmpty()) { continue }
109687             transGeomList.add(transformGeom);
109688           }
109689           return this._factory.buildGeometry(transGeomList)
109690         };
109691         GeometryTransformer.prototype.transformMultiPolygon = function transformMultiPolygon (geom, parent) {
109692             var this$1 = this;
109693
109694           var transGeomList = new ArrayList();
109695           for (var i = 0; i < geom.getNumGeometries(); i++) {
109696             var transformGeom = this$1.transformPolygon(geom.getGeometryN(i), geom);
109697             if (transformGeom === null) { continue }
109698             if (transformGeom.isEmpty()) { continue }
109699             transGeomList.add(transformGeom);
109700           }
109701           return this._factory.buildGeometry(transGeomList)
109702         };
109703         GeometryTransformer.prototype.copy = function copy (seq) {
109704           return seq.copy()
109705         };
109706         GeometryTransformer.prototype.transformGeometryCollection = function transformGeometryCollection (geom, parent) {
109707             var this$1 = this;
109708
109709           var transGeomList = new ArrayList();
109710           for (var i = 0; i < geom.getNumGeometries(); i++) {
109711             var transformGeom = this$1.transform(geom.getGeometryN(i));
109712             if (transformGeom === null) { continue }
109713             if (this$1._pruneEmptyGeometry && transformGeom.isEmpty()) { continue }
109714             transGeomList.add(transformGeom);
109715           }
109716           if (this._preserveGeometryCollectionType) { return this._factory.createGeometryCollection(GeometryFactory.toGeometryArray(transGeomList)) }
109717           return this._factory.buildGeometry(transGeomList)
109718         };
109719         GeometryTransformer.prototype.transform = function transform (inputGeom) {
109720           this._inputGeom = inputGeom;
109721           this._factory = inputGeom.getFactory();
109722           if (inputGeom instanceof Point$1) { return this.transformPoint(inputGeom, null) }
109723           if (inputGeom instanceof MultiPoint) { return this.transformMultiPoint(inputGeom, null) }
109724           if (inputGeom instanceof LinearRing) { return this.transformLinearRing(inputGeom, null) }
109725           if (inputGeom instanceof LineString) { return this.transformLineString(inputGeom, null) }
109726           if (inputGeom instanceof MultiLineString) { return this.transformMultiLineString(inputGeom, null) }
109727           if (inputGeom instanceof Polygon) { return this.transformPolygon(inputGeom, null) }
109728           if (inputGeom instanceof MultiPolygon) { return this.transformMultiPolygon(inputGeom, null) }
109729           if (inputGeom instanceof GeometryCollection) { return this.transformGeometryCollection(inputGeom, null) }
109730           throw new IllegalArgumentException('Unknown Geometry subtype: ' + inputGeom.getClass().getName())
109731         };
109732         GeometryTransformer.prototype.transformLinearRing = function transformLinearRing (geom, parent) {
109733           var seq = this.transformCoordinates(geom.getCoordinateSequence(), geom);
109734           if (seq === null) { return this._factory.createLinearRing(null) }
109735           var seqSize = seq.size();
109736           if (seqSize > 0 && seqSize < 4 && !this._preserveType) { return this._factory.createLineString(seq) }
109737           return this._factory.createLinearRing(seq)
109738         };
109739         GeometryTransformer.prototype.interfaces_ = function interfaces_ () {
109740           return []
109741         };
109742         GeometryTransformer.prototype.getClass = function getClass () {
109743           return GeometryTransformer
109744         };
109745
109746         var LineStringSnapper = function LineStringSnapper () {
109747           this._snapTolerance = 0.0;
109748           this._srcPts = null;
109749           this._seg = new LineSegment();
109750           this._allowSnappingToSourceVertices = false;
109751           this._isClosed = false;
109752           if (arguments[0] instanceof LineString && typeof arguments[1] === 'number') {
109753             var srcLine = arguments[0];
109754             var snapTolerance = arguments[1];
109755             LineStringSnapper.call(this, srcLine.getCoordinates(), snapTolerance);
109756           } else if (arguments[0] instanceof Array && typeof arguments[1] === 'number') {
109757             var srcPts = arguments[0];
109758             var snapTolerance$1 = arguments[1];
109759             this._srcPts = srcPts;
109760             this._isClosed = LineStringSnapper.isClosed(srcPts);
109761             this._snapTolerance = snapTolerance$1;
109762           }
109763         };
109764         LineStringSnapper.prototype.snapVertices = function snapVertices (srcCoords, snapPts) {
109765             var this$1 = this;
109766
109767           var end = this._isClosed ? srcCoords.size() - 1 : srcCoords.size();
109768           for (var i = 0; i < end; i++) {
109769             var srcPt = srcCoords.get(i);
109770             var snapVert = this$1.findSnapForVertex(srcPt, snapPts);
109771             if (snapVert !== null) {
109772               srcCoords.set(i, new Coordinate(snapVert));
109773               if (i === 0 && this$1._isClosed) { srcCoords.set(srcCoords.size() - 1, new Coordinate(snapVert)); }
109774             }
109775           }
109776         };
109777         LineStringSnapper.prototype.findSnapForVertex = function findSnapForVertex (pt, snapPts) {
109778             var this$1 = this;
109779
109780           for (var i = 0; i < snapPts.length; i++) {
109781             if (pt.equals2D(snapPts[i])) { return null }
109782             if (pt.distance(snapPts[i]) < this$1._snapTolerance) { return snapPts[i] }
109783           }
109784           return null
109785         };
109786         LineStringSnapper.prototype.snapTo = function snapTo (snapPts) {
109787           var coordList = new CoordinateList(this._srcPts);
109788           this.snapVertices(coordList, snapPts);
109789           this.snapSegments(coordList, snapPts);
109790           var newPts = coordList.toCoordinateArray();
109791           return newPts
109792         };
109793         LineStringSnapper.prototype.snapSegments = function snapSegments (srcCoords, snapPts) {
109794             var this$1 = this;
109795
109796           if (snapPts.length === 0) { return null }
109797           var distinctPtCount = snapPts.length;
109798           if (snapPts[0].equals2D(snapPts[snapPts.length - 1])) { distinctPtCount = snapPts.length - 1; }
109799           for (var i = 0; i < distinctPtCount; i++) {
109800             var snapPt = snapPts[i];
109801             var index = this$1.findSegmentIndexToSnap(snapPt, srcCoords);
109802             if (index >= 0) {
109803               srcCoords.add(index + 1, new Coordinate(snapPt), false);
109804             }
109805           }
109806         };
109807         LineStringSnapper.prototype.findSegmentIndexToSnap = function findSegmentIndexToSnap (snapPt, srcCoords) {
109808             var this$1 = this;
109809
109810           var minDist = Double.MAX_VALUE;
109811           var snapIndex = -1;
109812           for (var i = 0; i < srcCoords.size() - 1; i++) {
109813             this$1._seg.p0 = srcCoords.get(i);
109814             this$1._seg.p1 = srcCoords.get(i + 1);
109815             if (this$1._seg.p0.equals2D(snapPt) || this$1._seg.p1.equals2D(snapPt)) {
109816               if (this$1._allowSnappingToSourceVertices) { continue; } else { return -1 }
109817             }
109818             var dist = this$1._seg.distance(snapPt);
109819             if (dist < this$1._snapTolerance && dist < minDist) {
109820               minDist = dist;
109821               snapIndex = i;
109822             }
109823           }
109824           return snapIndex
109825         };
109826         LineStringSnapper.prototype.setAllowSnappingToSourceVertices = function setAllowSnappingToSourceVertices (allowSnappingToSourceVertices) {
109827           this._allowSnappingToSourceVertices = allowSnappingToSourceVertices;
109828         };
109829         LineStringSnapper.prototype.interfaces_ = function interfaces_ () {
109830           return []
109831         };
109832         LineStringSnapper.prototype.getClass = function getClass () {
109833           return LineStringSnapper
109834         };
109835         LineStringSnapper.isClosed = function isClosed (pts) {
109836           if (pts.length <= 1) { return false }
109837           return pts[0].equals2D(pts[pts.length - 1])
109838         };
109839
109840         var GeometrySnapper = function GeometrySnapper (srcGeom) {
109841           this._srcGeom = srcGeom || null;
109842         };
109843
109844         var staticAccessors$41 = { SNAP_PRECISION_FACTOR: { configurable: true } };
109845         GeometrySnapper.prototype.snapTo = function snapTo (snapGeom, snapTolerance) {
109846           var snapPts = this.extractTargetCoordinates(snapGeom);
109847           var snapTrans = new SnapTransformer(snapTolerance, snapPts);
109848           return snapTrans.transform(this._srcGeom)
109849         };
109850         GeometrySnapper.prototype.snapToSelf = function snapToSelf (snapTolerance, cleanResult) {
109851           var snapPts = this.extractTargetCoordinates(this._srcGeom);
109852           var snapTrans = new SnapTransformer(snapTolerance, snapPts, true);
109853           var snappedGeom = snapTrans.transform(this._srcGeom);
109854           var result = snappedGeom;
109855           if (cleanResult && hasInterface(result, Polygonal)) {
109856             result = snappedGeom.buffer(0);
109857           }
109858           return result
109859         };
109860         GeometrySnapper.prototype.computeSnapTolerance = function computeSnapTolerance (ringPts) {
109861           var minSegLen = this.computeMinimumSegmentLength(ringPts);
109862           var snapTol = minSegLen / 10;
109863           return snapTol
109864         };
109865         GeometrySnapper.prototype.extractTargetCoordinates = function extractTargetCoordinates (g) {
109866           var ptSet = new TreeSet();
109867           var pts = g.getCoordinates();
109868           for (var i = 0; i < pts.length; i++) {
109869             ptSet.add(pts[i]);
109870           }
109871           return ptSet.toArray(new Array(0).fill(null))
109872         };
109873         GeometrySnapper.prototype.computeMinimumSegmentLength = function computeMinimumSegmentLength (pts) {
109874           var minSegLen = Double.MAX_VALUE;
109875           for (var i = 0; i < pts.length - 1; i++) {
109876             var segLen = pts[i].distance(pts[i + 1]);
109877             if (segLen < minSegLen) { minSegLen = segLen; }
109878           }
109879           return minSegLen
109880         };
109881         GeometrySnapper.prototype.interfaces_ = function interfaces_ () {
109882           return []
109883         };
109884         GeometrySnapper.prototype.getClass = function getClass () {
109885           return GeometrySnapper
109886         };
109887         GeometrySnapper.snap = function snap (g0, g1, snapTolerance) {
109888           var snapGeom = new Array(2).fill(null);
109889           var snapper0 = new GeometrySnapper(g0);
109890           snapGeom[0] = snapper0.snapTo(g1, snapTolerance);
109891           var snapper1 = new GeometrySnapper(g1);
109892           snapGeom[1] = snapper1.snapTo(snapGeom[0], snapTolerance);
109893           return snapGeom
109894         };
109895         GeometrySnapper.computeOverlaySnapTolerance = function computeOverlaySnapTolerance () {
109896           if (arguments.length === 1) {
109897             var g = arguments[0];
109898             var snapTolerance = GeometrySnapper.computeSizeBasedSnapTolerance(g);
109899             var pm = g.getPrecisionModel();
109900             if (pm.getType() === PrecisionModel.FIXED) {
109901               var fixedSnapTol = 1 / pm.getScale() * 2 / 1.415;
109902               if (fixedSnapTol > snapTolerance) { snapTolerance = fixedSnapTol; }
109903             }
109904             return snapTolerance
109905           } else if (arguments.length === 2) {
109906             var g0 = arguments[0];
109907             var g1 = arguments[1];
109908             return Math.min(GeometrySnapper.computeOverlaySnapTolerance(g0), GeometrySnapper.computeOverlaySnapTolerance(g1))
109909           }
109910         };
109911         GeometrySnapper.computeSizeBasedSnapTolerance = function computeSizeBasedSnapTolerance (g) {
109912           var env = g.getEnvelopeInternal();
109913           var minDimension = Math.min(env.getHeight(), env.getWidth());
109914           var snapTol = minDimension * GeometrySnapper.SNAP_PRECISION_FACTOR;
109915           return snapTol
109916         };
109917         GeometrySnapper.snapToSelf = function snapToSelf (geom, snapTolerance, cleanResult) {
109918           var snapper0 = new GeometrySnapper(geom);
109919           return snapper0.snapToSelf(snapTolerance, cleanResult)
109920         };
109921         staticAccessors$41.SNAP_PRECISION_FACTOR.get = function () { return 1e-9 };
109922
109923         Object.defineProperties( GeometrySnapper, staticAccessors$41 );
109924
109925         var SnapTransformer = (function (GeometryTransformer$$1) {
109926           function SnapTransformer (snapTolerance, snapPts, isSelfSnap) {
109927             GeometryTransformer$$1.call(this);
109928             this._snapTolerance = snapTolerance || null;
109929             this._snapPts = snapPts || null;
109930             this._isSelfSnap = (isSelfSnap !== undefined) ? isSelfSnap : false;
109931           }
109932
109933           if ( GeometryTransformer$$1 ) { SnapTransformer.__proto__ = GeometryTransformer$$1; }
109934           SnapTransformer.prototype = Object.create( GeometryTransformer$$1 && GeometryTransformer$$1.prototype );
109935           SnapTransformer.prototype.constructor = SnapTransformer;
109936           SnapTransformer.prototype.snapLine = function snapLine (srcPts, snapPts) {
109937             var snapper = new LineStringSnapper(srcPts, this._snapTolerance);
109938             snapper.setAllowSnappingToSourceVertices(this._isSelfSnap);
109939             return snapper.snapTo(snapPts)
109940           };
109941           SnapTransformer.prototype.transformCoordinates = function transformCoordinates (coords, parent) {
109942             var srcPts = coords.toCoordinateArray();
109943             var newPts = this.snapLine(srcPts, this._snapPts);
109944             return this._factory.getCoordinateSequenceFactory().create(newPts)
109945           };
109946           SnapTransformer.prototype.interfaces_ = function interfaces_ () {
109947             return []
109948           };
109949           SnapTransformer.prototype.getClass = function getClass () {
109950             return SnapTransformer
109951           };
109952
109953           return SnapTransformer;
109954         }(GeometryTransformer));
109955
109956         var CommonBits = function CommonBits () {
109957           this._isFirst = true;
109958           this._commonMantissaBitsCount = 53;
109959           this._commonBits = 0;
109960           this._commonSignExp = null;
109961         };
109962         CommonBits.prototype.getCommon = function getCommon () {
109963           return Double.longBitsToDouble(this._commonBits)
109964         };
109965         CommonBits.prototype.add = function add (num) {
109966           var numBits = Double.doubleToLongBits(num);
109967           if (this._isFirst) {
109968             this._commonBits = numBits;
109969             this._commonSignExp = CommonBits.signExpBits(this._commonBits);
109970             this._isFirst = false;
109971             return null
109972           }
109973           var numSignExp = CommonBits.signExpBits(numBits);
109974           if (numSignExp !== this._commonSignExp) {
109975             this._commonBits = 0;
109976             return null
109977           }
109978           this._commonMantissaBitsCount = CommonBits.numCommonMostSigMantissaBits(this._commonBits, numBits);
109979           this._commonBits = CommonBits.zeroLowerBits(this._commonBits, 64 - (12 + this._commonMantissaBitsCount));
109980         };
109981         CommonBits.prototype.toString = function toString () {
109982           if (arguments.length === 1) {
109983             var bits = arguments[0];
109984             var x = Double.longBitsToDouble(bits);
109985             var numStr = Double.toBinaryString(bits);
109986             var padStr = '0000000000000000000000000000000000000000000000000000000000000000' + numStr;
109987             var bitStr = padStr.substring(padStr.length - 64);
109988             var str = bitStr.substring(0, 1) + '  ' + bitStr.substring(1, 12) + '(exp) ' + bitStr.substring(12) + ' [ ' + x + ' ]';
109989             return str
109990           }
109991         };
109992         CommonBits.prototype.interfaces_ = function interfaces_ () {
109993           return []
109994         };
109995         CommonBits.prototype.getClass = function getClass () {
109996           return CommonBits
109997         };
109998         CommonBits.getBit = function getBit (bits, i) {
109999           var mask = 1 << i;
110000           return (bits & mask) !== 0 ? 1 : 0
110001         };
110002         CommonBits.signExpBits = function signExpBits (num) {
110003           return num >> 52
110004         };
110005         CommonBits.zeroLowerBits = function zeroLowerBits (bits, nBits) {
110006           var invMask = (1 << nBits) - 1;
110007           var mask = ~invMask;
110008           var zeroed = bits & mask;
110009           return zeroed
110010         };
110011         CommonBits.numCommonMostSigMantissaBits = function numCommonMostSigMantissaBits (num1, num2) {
110012           var count = 0;
110013           for (var i = 52; i >= 0; i--) {
110014             if (CommonBits.getBit(num1, i) !== CommonBits.getBit(num2, i)) { return count }
110015             count++;
110016           }
110017           return 52
110018         };
110019
110020         var CommonBitsRemover = function CommonBitsRemover () {
110021           this._commonCoord = null;
110022           this._ccFilter = new CommonCoordinateFilter();
110023         };
110024
110025         var staticAccessors$42 = { CommonCoordinateFilter: { configurable: true },Translater: { configurable: true } };
110026         CommonBitsRemover.prototype.addCommonBits = function addCommonBits (geom) {
110027           var trans = new Translater(this._commonCoord);
110028           geom.apply(trans);
110029           geom.geometryChanged();
110030         };
110031         CommonBitsRemover.prototype.removeCommonBits = function removeCommonBits (geom) {
110032           if (this._commonCoord.x === 0.0 && this._commonCoord.y === 0.0) { return geom }
110033           var invCoord = new Coordinate(this._commonCoord);
110034           invCoord.x = -invCoord.x;
110035           invCoord.y = -invCoord.y;
110036           var trans = new Translater(invCoord);
110037           geom.apply(trans);
110038           geom.geometryChanged();
110039           return geom
110040         };
110041         CommonBitsRemover.prototype.getCommonCoordinate = function getCommonCoordinate () {
110042           return this._commonCoord
110043         };
110044         CommonBitsRemover.prototype.add = function add (geom) {
110045           geom.apply(this._ccFilter);
110046           this._commonCoord = this._ccFilter.getCommonCoordinate();
110047         };
110048         CommonBitsRemover.prototype.interfaces_ = function interfaces_ () {
110049           return []
110050         };
110051         CommonBitsRemover.prototype.getClass = function getClass () {
110052           return CommonBitsRemover
110053         };
110054         staticAccessors$42.CommonCoordinateFilter.get = function () { return CommonCoordinateFilter };
110055         staticAccessors$42.Translater.get = function () { return Translater };
110056
110057         Object.defineProperties( CommonBitsRemover, staticAccessors$42 );
110058
110059         var CommonCoordinateFilter = function CommonCoordinateFilter () {
110060           this._commonBitsX = new CommonBits();
110061           this._commonBitsY = new CommonBits();
110062         };
110063         CommonCoordinateFilter.prototype.filter = function filter (coord) {
110064           this._commonBitsX.add(coord.x);
110065           this._commonBitsY.add(coord.y);
110066         };
110067         CommonCoordinateFilter.prototype.getCommonCoordinate = function getCommonCoordinate () {
110068           return new Coordinate(this._commonBitsX.getCommon(), this._commonBitsY.getCommon())
110069         };
110070         CommonCoordinateFilter.prototype.interfaces_ = function interfaces_ () {
110071           return [CoordinateFilter]
110072         };
110073         CommonCoordinateFilter.prototype.getClass = function getClass () {
110074           return CommonCoordinateFilter
110075         };
110076
110077         var Translater = function Translater () {
110078           this.trans = null;
110079           var trans = arguments[0];
110080           this.trans = trans;
110081         };
110082         Translater.prototype.filter = function filter (seq, i) {
110083           var xp = seq.getOrdinate(i, 0) + this.trans.x;
110084           var yp = seq.getOrdinate(i, 1) + this.trans.y;
110085           seq.setOrdinate(i, 0, xp);
110086           seq.setOrdinate(i, 1, yp);
110087         };
110088         Translater.prototype.isDone = function isDone () {
110089           return false
110090         };
110091         Translater.prototype.isGeometryChanged = function isGeometryChanged () {
110092           return true
110093         };
110094         Translater.prototype.interfaces_ = function interfaces_ () {
110095           return [CoordinateSequenceFilter]
110096         };
110097         Translater.prototype.getClass = function getClass () {
110098           return Translater
110099         };
110100
110101         var SnapOverlayOp = function SnapOverlayOp (g1, g2) {
110102           this._geom = new Array(2).fill(null);
110103           this._snapTolerance = null;
110104           this._cbr = null;
110105           this._geom[0] = g1;
110106           this._geom[1] = g2;
110107           this.computeSnapTolerance();
110108         };
110109         SnapOverlayOp.prototype.selfSnap = function selfSnap (geom) {
110110           var snapper0 = new GeometrySnapper(geom);
110111           var snapGeom = snapper0.snapTo(geom, this._snapTolerance);
110112           return snapGeom
110113         };
110114         SnapOverlayOp.prototype.removeCommonBits = function removeCommonBits (geom) {
110115           this._cbr = new CommonBitsRemover();
110116           this._cbr.add(geom[0]);
110117           this._cbr.add(geom[1]);
110118           var remGeom = new Array(2).fill(null);
110119           remGeom[0] = this._cbr.removeCommonBits(geom[0].copy());
110120           remGeom[1] = this._cbr.removeCommonBits(geom[1].copy());
110121           return remGeom
110122         };
110123         SnapOverlayOp.prototype.prepareResult = function prepareResult (geom) {
110124           this._cbr.addCommonBits(geom);
110125           return geom
110126         };
110127         SnapOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110128           var prepGeom = this.snap(this._geom);
110129           var result = OverlayOp.overlayOp(prepGeom[0], prepGeom[1], opCode);
110130           return this.prepareResult(result)
110131         };
110132         SnapOverlayOp.prototype.checkValid = function checkValid (g) {
110133           if (!g.isValid()) {
110134             System.out.println('Snapped geometry is invalid');
110135           }
110136         };
110137         SnapOverlayOp.prototype.computeSnapTolerance = function computeSnapTolerance () {
110138           this._snapTolerance = GeometrySnapper.computeOverlaySnapTolerance(this._geom[0], this._geom[1]);
110139         };
110140         SnapOverlayOp.prototype.snap = function snap (geom) {
110141           var remGeom = this.removeCommonBits(geom);
110142           var snapGeom = GeometrySnapper.snap(remGeom[0], remGeom[1], this._snapTolerance);
110143           return snapGeom
110144         };
110145         SnapOverlayOp.prototype.interfaces_ = function interfaces_ () {
110146           return []
110147         };
110148         SnapOverlayOp.prototype.getClass = function getClass () {
110149           return SnapOverlayOp
110150         };
110151         SnapOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110152           var op = new SnapOverlayOp(g0, g1);
110153           return op.getResultGeometry(opCode)
110154         };
110155         SnapOverlayOp.union = function union (g0, g1) {
110156           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110157         };
110158         SnapOverlayOp.intersection = function intersection (g0, g1) {
110159           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110160         };
110161         SnapOverlayOp.symDifference = function symDifference (g0, g1) {
110162           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110163         };
110164         SnapOverlayOp.difference = function difference (g0, g1) {
110165           return SnapOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110166         };
110167
110168         var SnapIfNeededOverlayOp = function SnapIfNeededOverlayOp (g1, g2) {
110169           this._geom = new Array(2).fill(null);
110170           this._geom[0] = g1;
110171           this._geom[1] = g2;
110172         };
110173         SnapIfNeededOverlayOp.prototype.getResultGeometry = function getResultGeometry (opCode) {
110174           var result = null;
110175           var isSuccess = false;
110176           var savedException = null;
110177           try {
110178             result = OverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110179             var isValid = true;
110180             if (isValid) { isSuccess = true; }
110181           } catch (ex) {
110182             if (ex instanceof RuntimeException) {
110183               savedException = ex;
110184             } else { throw ex }
110185           } finally {}
110186           if (!isSuccess) {
110187             try {
110188               result = SnapOverlayOp.overlayOp(this._geom[0], this._geom[1], opCode);
110189             } catch (ex$1) {
110190               if (ex$1 instanceof RuntimeException) {
110191                 throw savedException
110192               } else { throw ex$1 }
110193             } finally {}
110194           }
110195           return result
110196         };
110197         SnapIfNeededOverlayOp.prototype.interfaces_ = function interfaces_ () {
110198           return []
110199         };
110200         SnapIfNeededOverlayOp.prototype.getClass = function getClass () {
110201           return SnapIfNeededOverlayOp
110202         };
110203         SnapIfNeededOverlayOp.overlayOp = function overlayOp (g0, g1, opCode) {
110204           var op = new SnapIfNeededOverlayOp(g0, g1);
110205           return op.getResultGeometry(opCode)
110206         };
110207         SnapIfNeededOverlayOp.union = function union (g0, g1) {
110208           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.UNION)
110209         };
110210         SnapIfNeededOverlayOp.intersection = function intersection (g0, g1) {
110211           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.INTERSECTION)
110212         };
110213         SnapIfNeededOverlayOp.symDifference = function symDifference (g0, g1) {
110214           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.SYMDIFFERENCE)
110215         };
110216         SnapIfNeededOverlayOp.difference = function difference (g0, g1) {
110217           return SnapIfNeededOverlayOp.overlayOp(g0, g1, OverlayOp.DIFFERENCE)
110218         };
110219
110220         var MonotoneChain$2 = function MonotoneChain () {
110221           this.mce = null;
110222           this.chainIndex = null;
110223           var mce = arguments[0];
110224           var chainIndex = arguments[1];
110225           this.mce = mce;
110226           this.chainIndex = chainIndex;
110227         };
110228         MonotoneChain$2.prototype.computeIntersections = function computeIntersections (mc, si) {
110229           this.mce.computeIntersectsForChain(this.chainIndex, mc.mce, mc.chainIndex, si);
110230         };
110231         MonotoneChain$2.prototype.interfaces_ = function interfaces_ () {
110232           return []
110233         };
110234         MonotoneChain$2.prototype.getClass = function getClass () {
110235           return MonotoneChain$2
110236         };
110237
110238         var SweepLineEvent = function SweepLineEvent () {
110239           this._label = null;
110240           this._xValue = null;
110241           this._eventType = null;
110242           this._insertEvent = null;
110243           this._deleteEventIndex = null;
110244           this._obj = null;
110245           if (arguments.length === 2) {
110246             var x = arguments[0];
110247             var insertEvent = arguments[1];
110248             this._eventType = SweepLineEvent.DELETE;
110249             this._xValue = x;
110250             this._insertEvent = insertEvent;
110251           } else if (arguments.length === 3) {
110252             var label = arguments[0];
110253             var x$1 = arguments[1];
110254             var obj = arguments[2];
110255             this._eventType = SweepLineEvent.INSERT;
110256             this._label = label;
110257             this._xValue = x$1;
110258             this._obj = obj;
110259           }
110260         };
110261
110262         var staticAccessors$43 = { INSERT: { configurable: true },DELETE: { configurable: true } };
110263         SweepLineEvent.prototype.isDelete = function isDelete () {
110264           return this._eventType === SweepLineEvent.DELETE
110265         };
110266         SweepLineEvent.prototype.setDeleteEventIndex = function setDeleteEventIndex (deleteEventIndex) {
110267           this._deleteEventIndex = deleteEventIndex;
110268         };
110269         SweepLineEvent.prototype.getObject = function getObject () {
110270           return this._obj
110271         };
110272         SweepLineEvent.prototype.compareTo = function compareTo (o) {
110273           var pe = o;
110274           if (this._xValue < pe._xValue) { return -1 }
110275           if (this._xValue > pe._xValue) { return 1 }
110276           if (this._eventType < pe._eventType) { return -1 }
110277           if (this._eventType > pe._eventType) { return 1 }
110278           return 0
110279         };
110280         SweepLineEvent.prototype.getInsertEvent = function getInsertEvent () {
110281           return this._insertEvent
110282         };
110283         SweepLineEvent.prototype.isInsert = function isInsert () {
110284           return this._eventType === SweepLineEvent.INSERT
110285         };
110286         SweepLineEvent.prototype.isSameLabel = function isSameLabel (ev) {
110287           if (this._label === null) { return false }
110288           return this._label === ev._label
110289         };
110290         SweepLineEvent.prototype.getDeleteEventIndex = function getDeleteEventIndex () {
110291           return this._deleteEventIndex
110292         };
110293         SweepLineEvent.prototype.interfaces_ = function interfaces_ () {
110294           return [Comparable]
110295         };
110296         SweepLineEvent.prototype.getClass = function getClass () {
110297           return SweepLineEvent
110298         };
110299         staticAccessors$43.INSERT.get = function () { return 1 };
110300         staticAccessors$43.DELETE.get = function () { return 2 };
110301
110302         Object.defineProperties( SweepLineEvent, staticAccessors$43 );
110303
110304         var EdgeSetIntersector = function EdgeSetIntersector () {};
110305
110306         EdgeSetIntersector.prototype.interfaces_ = function interfaces_ () {
110307           return []
110308         };
110309         EdgeSetIntersector.prototype.getClass = function getClass () {
110310           return EdgeSetIntersector
110311         };
110312
110313         var SegmentIntersector$2 = function SegmentIntersector () {
110314           this._hasIntersection = false;
110315           this._hasProper = false;
110316           this._hasProperInterior = false;
110317           this._properIntersectionPoint = null;
110318           this._li = null;
110319           this._includeProper = null;
110320           this._recordIsolated = null;
110321           this._isSelfIntersection = null;
110322           this._numIntersections = 0;
110323           this.numTests = 0;
110324           this._bdyNodes = null;
110325           this._isDone = false;
110326           this._isDoneWhenProperInt = false;
110327           var li = arguments[0];
110328           var includeProper = arguments[1];
110329           var recordIsolated = arguments[2];
110330           this._li = li;
110331           this._includeProper = includeProper;
110332           this._recordIsolated = recordIsolated;
110333         };
110334         SegmentIntersector$2.prototype.isTrivialIntersection = function isTrivialIntersection (e0, segIndex0, e1, segIndex1) {
110335           if (e0 === e1) {
110336             if (this._li.getIntersectionNum() === 1) {
110337               if (SegmentIntersector$2.isAdjacentSegments(segIndex0, segIndex1)) { return true }
110338               if (e0.isClosed()) {
110339                 var maxSegIndex = e0.getNumPoints() - 1;
110340                 if ((segIndex0 === 0 && segIndex1 === maxSegIndex) ||
110341                     (segIndex1 === 0 && segIndex0 === maxSegIndex)) {
110342                   return true
110343                 }
110344               }
110345             }
110346           }
110347           return false
110348         };
110349         SegmentIntersector$2.prototype.getProperIntersectionPoint = function getProperIntersectionPoint () {
110350           return this._properIntersectionPoint
110351         };
110352         SegmentIntersector$2.prototype.setIsDoneIfProperInt = function setIsDoneIfProperInt (isDoneWhenProperInt) {
110353           this._isDoneWhenProperInt = isDoneWhenProperInt;
110354         };
110355         SegmentIntersector$2.prototype.hasProperInteriorIntersection = function hasProperInteriorIntersection () {
110356           return this._hasProperInterior
110357         };
110358         SegmentIntersector$2.prototype.isBoundaryPointInternal = function isBoundaryPointInternal (li, bdyNodes) {
110359           for (var i = bdyNodes.iterator(); i.hasNext();) {
110360             var node = i.next();
110361             var pt = node.getCoordinate();
110362             if (li.isIntersection(pt)) { return true }
110363           }
110364           return false
110365         };
110366         SegmentIntersector$2.prototype.hasProperIntersection = function hasProperIntersection () {
110367           return this._hasProper
110368         };
110369         SegmentIntersector$2.prototype.hasIntersection = function hasIntersection () {
110370           return this._hasIntersection
110371         };
110372         SegmentIntersector$2.prototype.isDone = function isDone () {
110373           return this._isDone
110374         };
110375         SegmentIntersector$2.prototype.isBoundaryPoint = function isBoundaryPoint (li, bdyNodes) {
110376           if (bdyNodes === null) { return false }
110377           if (this.isBoundaryPointInternal(li, bdyNodes[0])) { return true }
110378           if (this.isBoundaryPointInternal(li, bdyNodes[1])) { return true }
110379           return false
110380         };
110381         SegmentIntersector$2.prototype.setBoundaryNodes = function setBoundaryNodes (bdyNodes0, bdyNodes1) {
110382           this._bdyNodes = new Array(2).fill(null);
110383           this._bdyNodes[0] = bdyNodes0;
110384           this._bdyNodes[1] = bdyNodes1;
110385         };
110386         SegmentIntersector$2.prototype.addIntersections = function addIntersections (e0, segIndex0, e1, segIndex1) {
110387           if (e0 === e1 && segIndex0 === segIndex1) { return null }
110388           this.numTests++;
110389           var p00 = e0.getCoordinates()[segIndex0];
110390           var p01 = e0.getCoordinates()[segIndex0 + 1];
110391           var p10 = e1.getCoordinates()[segIndex1];
110392           var p11 = e1.getCoordinates()[segIndex1 + 1];
110393           this._li.computeIntersection(p00, p01, p10, p11);
110394           if (this._li.hasIntersection()) {
110395             if (this._recordIsolated) {
110396               e0.setIsolated(false);
110397               e1.setIsolated(false);
110398             }
110399             this._numIntersections++;
110400             if (!this.isTrivialIntersection(e0, segIndex0, e1, segIndex1)) {
110401               this._hasIntersection = true;
110402               if (this._includeProper || !this._li.isProper()) {
110403                 e0.addIntersections(this._li, segIndex0, 0);
110404                 e1.addIntersections(this._li, segIndex1, 1);
110405               }
110406               if (this._li.isProper()) {
110407                 this._properIntersectionPoint = this._li.getIntersection(0).copy();
110408                 this._hasProper = true;
110409                 if (this._isDoneWhenProperInt) {
110410                   this._isDone = true;
110411                 }
110412                 if (!this.isBoundaryPoint(this._li, this._bdyNodes)) { this._hasProperInterior = true; }
110413               }
110414             }
110415           }
110416         };
110417         SegmentIntersector$2.prototype.interfaces_ = function interfaces_ () {
110418           return []
110419         };
110420         SegmentIntersector$2.prototype.getClass = function getClass () {
110421           return SegmentIntersector$2
110422         };
110423         SegmentIntersector$2.isAdjacentSegments = function isAdjacentSegments (i1, i2) {
110424           return Math.abs(i1 - i2) === 1
110425         };
110426
110427         var SimpleMCSweepLineIntersector = (function (EdgeSetIntersector$$1) {
110428           function SimpleMCSweepLineIntersector () {
110429             EdgeSetIntersector$$1.call(this);
110430             this.events = new ArrayList();
110431             this.nOverlaps = null;
110432           }
110433
110434           if ( EdgeSetIntersector$$1 ) { SimpleMCSweepLineIntersector.__proto__ = EdgeSetIntersector$$1; }
110435           SimpleMCSweepLineIntersector.prototype = Object.create( EdgeSetIntersector$$1 && EdgeSetIntersector$$1.prototype );
110436           SimpleMCSweepLineIntersector.prototype.constructor = SimpleMCSweepLineIntersector;
110437           SimpleMCSweepLineIntersector.prototype.prepareEvents = function prepareEvents () {
110438             var this$1 = this;
110439
110440             Collections.sort(this.events);
110441             for (var i = 0; i < this.events.size(); i++) {
110442               var ev = this$1.events.get(i);
110443               if (ev.isDelete()) {
110444                 ev.getInsertEvent().setDeleteEventIndex(i);
110445               }
110446             }
110447           };
110448           SimpleMCSweepLineIntersector.prototype.computeIntersections = function computeIntersections () {
110449             var this$1 = this;
110450
110451             if (arguments.length === 1) {
110452               var si = arguments[0];
110453               this.nOverlaps = 0;
110454               this.prepareEvents();
110455               for (var i = 0; i < this.events.size(); i++) {
110456                 var ev = this$1.events.get(i);
110457                 if (ev.isInsert()) {
110458                   this$1.processOverlaps(i, ev.getDeleteEventIndex(), ev, si);
110459                 }
110460                 if (si.isDone()) {
110461                   break
110462                 }
110463               }
110464             } else if (arguments.length === 3) {
110465               if (arguments[2] instanceof SegmentIntersector$2 && (hasInterface(arguments[0], List) && hasInterface(arguments[1], List))) {
110466                 var edges0 = arguments[0];
110467                 var edges1 = arguments[1];
110468                 var si$1 = arguments[2];
110469                 this.addEdges(edges0, edges0);
110470                 this.addEdges(edges1, edges1);
110471                 this.computeIntersections(si$1);
110472               } else if (typeof arguments[2] === 'boolean' && (hasInterface(arguments[0], List) && arguments[1] instanceof SegmentIntersector$2)) {
110473                 var edges = arguments[0];
110474                 var si$2 = arguments[1];
110475                 var testAllSegments = arguments[2];
110476                 if (testAllSegments) { this.addEdges(edges, null); } else { this.addEdges(edges); }
110477                 this.computeIntersections(si$2);
110478               }
110479             }
110480           };
110481           SimpleMCSweepLineIntersector.prototype.addEdge = function addEdge (edge, edgeSet) {
110482             var this$1 = this;
110483
110484             var mce = edge.getMonotoneChainEdge();
110485             var startIndex = mce.getStartIndexes();
110486             for (var i = 0; i < startIndex.length - 1; i++) {
110487               var mc = new MonotoneChain$2(mce, i);
110488               var insertEvent = new SweepLineEvent(edgeSet, mce.getMinX(i), mc);
110489               this$1.events.add(insertEvent);
110490               this$1.events.add(new SweepLineEvent(mce.getMaxX(i), insertEvent));
110491             }
110492           };
110493           SimpleMCSweepLineIntersector.prototype.processOverlaps = function processOverlaps (start, end, ev0, si) {
110494             var this$1 = this;
110495
110496             var mc0 = ev0.getObject();
110497             for (var i = start; i < end; i++) {
110498               var ev1 = this$1.events.get(i);
110499               if (ev1.isInsert()) {
110500                 var mc1 = ev1.getObject();
110501                 if (!ev0.isSameLabel(ev1)) {
110502                   mc0.computeIntersections(mc1, si);
110503                   this$1.nOverlaps++;
110504                 }
110505               }
110506             }
110507           };
110508           SimpleMCSweepLineIntersector.prototype.addEdges = function addEdges () {
110509             var this$1 = this;
110510
110511             if (arguments.length === 1) {
110512               var edges = arguments[0];
110513               for (var i = edges.iterator(); i.hasNext();) {
110514                 var edge = i.next();
110515                 this$1.addEdge(edge, edge);
110516               }
110517             } else if (arguments.length === 2) {
110518               var edges$1 = arguments[0];
110519               var edgeSet = arguments[1];
110520               for (var i$1 = edges$1.iterator(); i$1.hasNext();) {
110521                 var edge$1 = i$1.next();
110522                 this$1.addEdge(edge$1, edgeSet);
110523               }
110524             }
110525           };
110526           SimpleMCSweepLineIntersector.prototype.interfaces_ = function interfaces_ () {
110527             return []
110528           };
110529           SimpleMCSweepLineIntersector.prototype.getClass = function getClass () {
110530             return SimpleMCSweepLineIntersector
110531           };
110532
110533           return SimpleMCSweepLineIntersector;
110534         }(EdgeSetIntersector));
110535
110536         var IntervalRTreeNode = function IntervalRTreeNode () {
110537           this._min = Double.POSITIVE_INFINITY;
110538           this._max = Double.NEGATIVE_INFINITY;
110539         };
110540
110541         var staticAccessors$45 = { NodeComparator: { configurable: true } };
110542         IntervalRTreeNode.prototype.getMin = function getMin () {
110543           return this._min
110544         };
110545         IntervalRTreeNode.prototype.intersects = function intersects (queryMin, queryMax) {
110546           if (this._min > queryMax || this._max < queryMin) { return false }
110547           return true
110548         };
110549         IntervalRTreeNode.prototype.getMax = function getMax () {
110550           return this._max
110551         };
110552         IntervalRTreeNode.prototype.toString = function toString () {
110553           return WKTWriter.toLineString(new Coordinate(this._min, 0), new Coordinate(this._max, 0))
110554         };
110555         IntervalRTreeNode.prototype.interfaces_ = function interfaces_ () {
110556           return []
110557         };
110558         IntervalRTreeNode.prototype.getClass = function getClass () {
110559           return IntervalRTreeNode
110560         };
110561         staticAccessors$45.NodeComparator.get = function () { return NodeComparator };
110562
110563         Object.defineProperties( IntervalRTreeNode, staticAccessors$45 );
110564
110565         var NodeComparator = function NodeComparator () {};
110566
110567         NodeComparator.prototype.compare = function compare (o1, o2) {
110568           var n1 = o1;
110569           var n2 = o2;
110570           var mid1 = (n1._min + n1._max) / 2;
110571           var mid2 = (n2._min + n2._max) / 2;
110572           if (mid1 < mid2) { return -1 }
110573           if (mid1 > mid2) { return 1 }
110574           return 0
110575         };
110576         NodeComparator.prototype.interfaces_ = function interfaces_ () {
110577           return [Comparator]
110578         };
110579         NodeComparator.prototype.getClass = function getClass () {
110580           return NodeComparator
110581         };
110582
110583         var IntervalRTreeLeafNode = (function (IntervalRTreeNode$$1) {
110584           function IntervalRTreeLeafNode () {
110585             IntervalRTreeNode$$1.call(this);
110586             this._item = null;
110587             var min = arguments[0];
110588             var max = arguments[1];
110589             var item = arguments[2];
110590             this._min = min;
110591             this._max = max;
110592             this._item = item;
110593           }
110594
110595           if ( IntervalRTreeNode$$1 ) { IntervalRTreeLeafNode.__proto__ = IntervalRTreeNode$$1; }
110596           IntervalRTreeLeafNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110597           IntervalRTreeLeafNode.prototype.constructor = IntervalRTreeLeafNode;
110598           IntervalRTreeLeafNode.prototype.query = function query (queryMin, queryMax, visitor) {
110599             if (!this.intersects(queryMin, queryMax)) { return null }
110600             visitor.visitItem(this._item);
110601           };
110602           IntervalRTreeLeafNode.prototype.interfaces_ = function interfaces_ () {
110603             return []
110604           };
110605           IntervalRTreeLeafNode.prototype.getClass = function getClass () {
110606             return IntervalRTreeLeafNode
110607           };
110608
110609           return IntervalRTreeLeafNode;
110610         }(IntervalRTreeNode));
110611
110612         var IntervalRTreeBranchNode = (function (IntervalRTreeNode$$1) {
110613           function IntervalRTreeBranchNode () {
110614             IntervalRTreeNode$$1.call(this);
110615             this._node1 = null;
110616             this._node2 = null;
110617             var n1 = arguments[0];
110618             var n2 = arguments[1];
110619             this._node1 = n1;
110620             this._node2 = n2;
110621             this.buildExtent(this._node1, this._node2);
110622           }
110623
110624           if ( IntervalRTreeNode$$1 ) { IntervalRTreeBranchNode.__proto__ = IntervalRTreeNode$$1; }
110625           IntervalRTreeBranchNode.prototype = Object.create( IntervalRTreeNode$$1 && IntervalRTreeNode$$1.prototype );
110626           IntervalRTreeBranchNode.prototype.constructor = IntervalRTreeBranchNode;
110627           IntervalRTreeBranchNode.prototype.buildExtent = function buildExtent (n1, n2) {
110628             this._min = Math.min(n1._min, n2._min);
110629             this._max = Math.max(n1._max, n2._max);
110630           };
110631           IntervalRTreeBranchNode.prototype.query = function query (queryMin, queryMax, visitor) {
110632             if (!this.intersects(queryMin, queryMax)) {
110633               return null
110634             }
110635             if (this._node1 !== null) { this._node1.query(queryMin, queryMax, visitor); }
110636             if (this._node2 !== null) { this._node2.query(queryMin, queryMax, visitor); }
110637           };
110638           IntervalRTreeBranchNode.prototype.interfaces_ = function interfaces_ () {
110639             return []
110640           };
110641           IntervalRTreeBranchNode.prototype.getClass = function getClass () {
110642             return IntervalRTreeBranchNode
110643           };
110644
110645           return IntervalRTreeBranchNode;
110646         }(IntervalRTreeNode));
110647
110648         var SortedPackedIntervalRTree = function SortedPackedIntervalRTree () {
110649           this._leaves = new ArrayList();
110650           this._root = null;
110651           this._level = 0;
110652         };
110653         SortedPackedIntervalRTree.prototype.buildTree = function buildTree () {
110654             var this$1 = this;
110655
110656           Collections.sort(this._leaves, new IntervalRTreeNode.NodeComparator());
110657           var src = this._leaves;
110658           var temp = null;
110659           var dest = new ArrayList();
110660           while (true) {
110661             this$1.buildLevel(src, dest);
110662             if (dest.size() === 1) { return dest.get(0) }
110663             temp = src;
110664             src = dest;
110665             dest = temp;
110666           }
110667         };
110668         SortedPackedIntervalRTree.prototype.insert = function insert (min, max, item) {
110669           if (this._root !== null) { throw new Error('Index cannot be added to once it has been queried') }
110670           this._leaves.add(new IntervalRTreeLeafNode(min, max, item));
110671         };
110672         SortedPackedIntervalRTree.prototype.query = function query (min, max, visitor) {
110673           this.init();
110674           this._root.query(min, max, visitor);
110675         };
110676         SortedPackedIntervalRTree.prototype.buildRoot = function buildRoot () {
110677           if (this._root !== null) { return null }
110678           this._root = this.buildTree();
110679         };
110680         SortedPackedIntervalRTree.prototype.printNode = function printNode (node) {
110681           System.out.println(WKTWriter.toLineString(new Coordinate(node._min, this._level), new Coordinate(node._max, this._level)));
110682         };
110683         SortedPackedIntervalRTree.prototype.init = function init () {
110684           if (this._root !== null) { return null }
110685           this.buildRoot();
110686         };
110687         SortedPackedIntervalRTree.prototype.buildLevel = function buildLevel (src, dest) {
110688           this._level++;
110689           dest.clear();
110690           for (var i = 0; i < src.size(); i += 2) {
110691             var n1 = src.get(i);
110692             var n2 = i + 1 < src.size() ? src.get(i) : null;
110693             if (n2 === null) {
110694               dest.add(n1);
110695             } else {
110696               var node = new IntervalRTreeBranchNode(src.get(i), src.get(i + 1));
110697               dest.add(node);
110698             }
110699           }
110700         };
110701         SortedPackedIntervalRTree.prototype.interfaces_ = function interfaces_ () {
110702           return []
110703         };
110704         SortedPackedIntervalRTree.prototype.getClass = function getClass () {
110705           return SortedPackedIntervalRTree
110706         };
110707
110708         var ArrayListVisitor = function ArrayListVisitor () {
110709           this._items = new ArrayList();
110710         };
110711         ArrayListVisitor.prototype.visitItem = function visitItem (item) {
110712           this._items.add(item);
110713         };
110714         ArrayListVisitor.prototype.getItems = function getItems () {
110715           return this._items
110716         };
110717         ArrayListVisitor.prototype.interfaces_ = function interfaces_ () {
110718           return [ItemVisitor]
110719         };
110720         ArrayListVisitor.prototype.getClass = function getClass () {
110721           return ArrayListVisitor
110722         };
110723
110724         var IndexedPointInAreaLocator = function IndexedPointInAreaLocator () {
110725           this._index = null;
110726           var g = arguments[0];
110727           if (!hasInterface(g, Polygonal)) { throw new IllegalArgumentException('Argument must be Polygonal') }
110728           this._index = new IntervalIndexedGeometry(g);
110729         };
110730
110731         var staticAccessors$44 = { SegmentVisitor: { configurable: true },IntervalIndexedGeometry: { configurable: true } };
110732         IndexedPointInAreaLocator.prototype.locate = function locate (p) {
110733           var rcc = new RayCrossingCounter(p);
110734           var visitor = new SegmentVisitor(rcc);
110735           this._index.query(p.y, p.y, visitor);
110736           return rcc.getLocation()
110737         };
110738         IndexedPointInAreaLocator.prototype.interfaces_ = function interfaces_ () {
110739           return [PointOnGeometryLocator]
110740         };
110741         IndexedPointInAreaLocator.prototype.getClass = function getClass () {
110742           return IndexedPointInAreaLocator
110743         };
110744         staticAccessors$44.SegmentVisitor.get = function () { return SegmentVisitor };
110745         staticAccessors$44.IntervalIndexedGeometry.get = function () { return IntervalIndexedGeometry };
110746
110747         Object.defineProperties( IndexedPointInAreaLocator, staticAccessors$44 );
110748
110749         var SegmentVisitor = function SegmentVisitor () {
110750           this._counter = null;
110751           var counter = arguments[0];
110752           this._counter = counter;
110753         };
110754         SegmentVisitor.prototype.visitItem = function visitItem (item) {
110755           var seg = item;
110756           this._counter.countSegment(seg.getCoordinate(0), seg.getCoordinate(1));
110757         };
110758         SegmentVisitor.prototype.interfaces_ = function interfaces_ () {
110759           return [ItemVisitor]
110760         };
110761         SegmentVisitor.prototype.getClass = function getClass () {
110762           return SegmentVisitor
110763         };
110764
110765         var IntervalIndexedGeometry = function IntervalIndexedGeometry () {
110766           this._index = new SortedPackedIntervalRTree();
110767           var geom = arguments[0];
110768           this.init(geom);
110769         };
110770         IntervalIndexedGeometry.prototype.init = function init (geom) {
110771             var this$1 = this;
110772
110773           var lines = LinearComponentExtracter.getLines(geom);
110774           for (var i = lines.iterator(); i.hasNext();) {
110775             var line = i.next();
110776             var pts = line.getCoordinates();
110777             this$1.addLine(pts);
110778           }
110779         };
110780         IntervalIndexedGeometry.prototype.addLine = function addLine (pts) {
110781             var this$1 = this;
110782
110783           for (var i = 1; i < pts.length; i++) {
110784             var seg = new LineSegment(pts[i - 1], pts[i]);
110785             var min = Math.min(seg.p0.y, seg.p1.y);
110786             var max = Math.max(seg.p0.y, seg.p1.y);
110787             this$1._index.insert(min, max, seg);
110788           }
110789         };
110790         IntervalIndexedGeometry.prototype.query = function query () {
110791           if (arguments.length === 2) {
110792             var min = arguments[0];
110793             var max = arguments[1];
110794             var visitor = new ArrayListVisitor();
110795             this._index.query(min, max, visitor);
110796             return visitor.getItems()
110797           } else if (arguments.length === 3) {
110798             var min$1 = arguments[0];
110799             var max$1 = arguments[1];
110800             var visitor$1 = arguments[2];
110801             this._index.query(min$1, max$1, visitor$1);
110802           }
110803         };
110804         IntervalIndexedGeometry.prototype.interfaces_ = function interfaces_ () {
110805           return []
110806         };
110807         IntervalIndexedGeometry.prototype.getClass = function getClass () {
110808           return IntervalIndexedGeometry
110809         };
110810
110811         var GeometryGraph = (function (PlanarGraph$$1) {
110812           function GeometryGraph () {
110813             PlanarGraph$$1.call(this);
110814             this._parentGeom = null;
110815             this._lineEdgeMap = new HashMap();
110816             this._boundaryNodeRule = null;
110817             this._useBoundaryDeterminationRule = true;
110818             this._argIndex = null;
110819             this._boundaryNodes = null;
110820             this._hasTooFewPoints = false;
110821             this._invalidPoint = null;
110822             this._areaPtLocator = null;
110823             this._ptLocator = new PointLocator();
110824             if (arguments.length === 2) {
110825               var argIndex = arguments[0];
110826               var parentGeom = arguments[1];
110827               var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
110828               this._argIndex = argIndex;
110829               this._parentGeom = parentGeom;
110830               this._boundaryNodeRule = boundaryNodeRule;
110831               if (parentGeom !== null) {
110832                 this.add(parentGeom);
110833               }
110834             } else if (arguments.length === 3) {
110835               var argIndex$1 = arguments[0];
110836               var parentGeom$1 = arguments[1];
110837               var boundaryNodeRule$1 = arguments[2];
110838               this._argIndex = argIndex$1;
110839               this._parentGeom = parentGeom$1;
110840               this._boundaryNodeRule = boundaryNodeRule$1;
110841               if (parentGeom$1 !== null) {
110842                 this.add(parentGeom$1);
110843               }
110844             }
110845           }
110846
110847           if ( PlanarGraph$$1 ) { GeometryGraph.__proto__ = PlanarGraph$$1; }
110848           GeometryGraph.prototype = Object.create( PlanarGraph$$1 && PlanarGraph$$1.prototype );
110849           GeometryGraph.prototype.constructor = GeometryGraph;
110850           GeometryGraph.prototype.insertBoundaryPoint = function insertBoundaryPoint (argIndex, coord) {
110851             var n = this._nodes.addNode(coord);
110852             var lbl = n.getLabel();
110853             var boundaryCount = 1;
110854             var loc = Location.NONE;
110855             loc = lbl.getLocation(argIndex, Position.ON);
110856             if (loc === Location.BOUNDARY) { boundaryCount++; }
110857             var newLoc = GeometryGraph.determineBoundary(this._boundaryNodeRule, boundaryCount);
110858             lbl.setLocation(argIndex, newLoc);
110859           };
110860           GeometryGraph.prototype.computeSelfNodes = function computeSelfNodes () {
110861             if (arguments.length === 2) {
110862               var li = arguments[0];
110863               var computeRingSelfNodes = arguments[1];
110864               return this.computeSelfNodes(li, computeRingSelfNodes, false)
110865             } else if (arguments.length === 3) {
110866               var li$1 = arguments[0];
110867               var computeRingSelfNodes$1 = arguments[1];
110868               var isDoneIfProperInt = arguments[2];
110869               var si = new SegmentIntersector$2(li$1, true, false);
110870               si.setIsDoneIfProperInt(isDoneIfProperInt);
110871               var esi = this.createEdgeSetIntersector();
110872               var isRings = this._parentGeom instanceof LinearRing || this._parentGeom instanceof Polygon || this._parentGeom instanceof MultiPolygon;
110873               var computeAllSegments = computeRingSelfNodes$1 || !isRings;
110874               esi.computeIntersections(this._edges, si, computeAllSegments);
110875               this.addSelfIntersectionNodes(this._argIndex);
110876               return si
110877             }
110878           };
110879           GeometryGraph.prototype.computeSplitEdges = function computeSplitEdges (edgelist) {
110880             for (var i = this._edges.iterator(); i.hasNext();) {
110881               var e = i.next();
110882               e.eiList.addSplitEdges(edgelist);
110883             }
110884           };
110885           GeometryGraph.prototype.computeEdgeIntersections = function computeEdgeIntersections (g, li, includeProper) {
110886             var si = new SegmentIntersector$2(li, includeProper, true);
110887             si.setBoundaryNodes(this.getBoundaryNodes(), g.getBoundaryNodes());
110888             var esi = this.createEdgeSetIntersector();
110889             esi.computeIntersections(this._edges, g._edges, si);
110890             return si
110891           };
110892           GeometryGraph.prototype.getGeometry = function getGeometry () {
110893             return this._parentGeom
110894           };
110895           GeometryGraph.prototype.getBoundaryNodeRule = function getBoundaryNodeRule () {
110896             return this._boundaryNodeRule
110897           };
110898           GeometryGraph.prototype.hasTooFewPoints = function hasTooFewPoints () {
110899             return this._hasTooFewPoints
110900           };
110901           GeometryGraph.prototype.addPoint = function addPoint () {
110902             if (arguments[0] instanceof Point$1) {
110903               var p = arguments[0];
110904               var coord = p.getCoordinate();
110905               this.insertPoint(this._argIndex, coord, Location.INTERIOR);
110906             } else if (arguments[0] instanceof Coordinate) {
110907               var pt = arguments[0];
110908               this.insertPoint(this._argIndex, pt, Location.INTERIOR);
110909             }
110910           };
110911           GeometryGraph.prototype.addPolygon = function addPolygon (p) {
110912             var this$1 = this;
110913
110914             this.addPolygonRing(p.getExteriorRing(), Location.EXTERIOR, Location.INTERIOR);
110915             for (var i = 0; i < p.getNumInteriorRing(); i++) {
110916               var hole = p.getInteriorRingN(i);
110917               this$1.addPolygonRing(hole, Location.INTERIOR, Location.EXTERIOR);
110918             }
110919           };
110920           GeometryGraph.prototype.addEdge = function addEdge (e) {
110921             this.insertEdge(e);
110922             var coord = e.getCoordinates();
110923             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110924             this.insertPoint(this._argIndex, coord[coord.length - 1], Location.BOUNDARY);
110925           };
110926           GeometryGraph.prototype.addLineString = function addLineString (line) {
110927             var coord = CoordinateArrays.removeRepeatedPoints(line.getCoordinates());
110928             if (coord.length < 2) {
110929               this._hasTooFewPoints = true;
110930               this._invalidPoint = coord[0];
110931               return null
110932             }
110933             var e = new Edge(coord, new Label(this._argIndex, Location.INTERIOR));
110934             this._lineEdgeMap.put(line, e);
110935             this.insertEdge(e);
110936             Assert.isTrue(coord.length >= 2, 'found LineString with single point');
110937             this.insertBoundaryPoint(this._argIndex, coord[0]);
110938             this.insertBoundaryPoint(this._argIndex, coord[coord.length - 1]);
110939           };
110940           GeometryGraph.prototype.getInvalidPoint = function getInvalidPoint () {
110941             return this._invalidPoint
110942           };
110943           GeometryGraph.prototype.getBoundaryPoints = function getBoundaryPoints () {
110944             var coll = this.getBoundaryNodes();
110945             var pts = new Array(coll.size()).fill(null);
110946             var i = 0;
110947             for (var it = coll.iterator(); it.hasNext();) {
110948               var node = it.next();
110949               pts[i++] = node.getCoordinate().copy();
110950             }
110951             return pts
110952           };
110953           GeometryGraph.prototype.getBoundaryNodes = function getBoundaryNodes () {
110954             if (this._boundaryNodes === null) { this._boundaryNodes = this._nodes.getBoundaryNodes(this._argIndex); }
110955             return this._boundaryNodes
110956           };
110957           GeometryGraph.prototype.addSelfIntersectionNode = function addSelfIntersectionNode (argIndex, coord, loc) {
110958             if (this.isBoundaryNode(argIndex, coord)) { return null }
110959             if (loc === Location.BOUNDARY && this._useBoundaryDeterminationRule) { this.insertBoundaryPoint(argIndex, coord); } else { this.insertPoint(argIndex, coord, loc); }
110960           };
110961           GeometryGraph.prototype.addPolygonRing = function addPolygonRing (lr, cwLeft, cwRight) {
110962             if (lr.isEmpty()) { return null }
110963             var coord = CoordinateArrays.removeRepeatedPoints(lr.getCoordinates());
110964             if (coord.length < 4) {
110965               this._hasTooFewPoints = true;
110966               this._invalidPoint = coord[0];
110967               return null
110968             }
110969             var left = cwLeft;
110970             var right = cwRight;
110971             if (CGAlgorithms.isCCW(coord)) {
110972               left = cwRight;
110973               right = cwLeft;
110974             }
110975             var e = new Edge(coord, new Label(this._argIndex, Location.BOUNDARY, left, right));
110976             this._lineEdgeMap.put(lr, e);
110977             this.insertEdge(e);
110978             this.insertPoint(this._argIndex, coord[0], Location.BOUNDARY);
110979           };
110980           GeometryGraph.prototype.insertPoint = function insertPoint (argIndex, coord, onLocation) {
110981             var n = this._nodes.addNode(coord);
110982             var lbl = n.getLabel();
110983             if (lbl === null) {
110984               n._label = new Label(argIndex, onLocation);
110985             } else { lbl.setLocation(argIndex, onLocation); }
110986           };
110987           GeometryGraph.prototype.createEdgeSetIntersector = function createEdgeSetIntersector () {
110988             return new SimpleMCSweepLineIntersector()
110989           };
110990           GeometryGraph.prototype.addSelfIntersectionNodes = function addSelfIntersectionNodes (argIndex) {
110991             var this$1 = this;
110992
110993             for (var i = this._edges.iterator(); i.hasNext();) {
110994               var e = i.next();
110995               var eLoc = e.getLabel().getLocation(argIndex);
110996               for (var eiIt = e.eiList.iterator(); eiIt.hasNext();) {
110997                 var ei = eiIt.next();
110998                 this$1.addSelfIntersectionNode(argIndex, ei.coord, eLoc);
110999               }
111000             }
111001           };
111002           GeometryGraph.prototype.add = function add () {
111003             if (arguments.length === 1) {
111004               var g = arguments[0];
111005               if (g.isEmpty()) { return null }
111006               if (g instanceof MultiPolygon) { this._useBoundaryDeterminationRule = false; }
111007               if (g instanceof Polygon) { this.addPolygon(g); }
111008               else if (g instanceof LineString) { this.addLineString(g); }
111009               else if (g instanceof Point$1) { this.addPoint(g); }
111010               else if (g instanceof MultiPoint) { this.addCollection(g); }
111011               else if (g instanceof MultiLineString) { this.addCollection(g); }
111012               else if (g instanceof MultiPolygon) { this.addCollection(g); }
111013               else if (g instanceof GeometryCollection) { this.addCollection(g); }
111014               else { throw new Error(g.getClass().getName()) }
111015             } else { return PlanarGraph$$1.prototype.add.apply(this, arguments) }
111016           };
111017           GeometryGraph.prototype.addCollection = function addCollection (gc) {
111018             var this$1 = this;
111019
111020             for (var i = 0; i < gc.getNumGeometries(); i++) {
111021               var g = gc.getGeometryN(i);
111022               this$1.add(g);
111023             }
111024           };
111025           GeometryGraph.prototype.locate = function locate (pt) {
111026             if (hasInterface(this._parentGeom, Polygonal) && this._parentGeom.getNumGeometries() > 50) {
111027               if (this._areaPtLocator === null) {
111028                 this._areaPtLocator = new IndexedPointInAreaLocator(this._parentGeom);
111029               }
111030               return this._areaPtLocator.locate(pt)
111031             }
111032             return this._ptLocator.locate(pt, this._parentGeom)
111033           };
111034           GeometryGraph.prototype.findEdge = function findEdge () {
111035             if (arguments.length === 1) {
111036               var line = arguments[0];
111037               return this._lineEdgeMap.get(line)
111038             } else { return PlanarGraph$$1.prototype.findEdge.apply(this, arguments) }
111039           };
111040           GeometryGraph.prototype.interfaces_ = function interfaces_ () {
111041             return []
111042           };
111043           GeometryGraph.prototype.getClass = function getClass () {
111044             return GeometryGraph
111045           };
111046           GeometryGraph.determineBoundary = function determineBoundary (boundaryNodeRule, boundaryCount) {
111047             return boundaryNodeRule.isInBoundary(boundaryCount) ? Location.BOUNDARY : Location.INTERIOR
111048           };
111049
111050           return GeometryGraph;
111051         }(PlanarGraph));
111052
111053         var GeometryGraphOp = function GeometryGraphOp () {
111054           this._li = new RobustLineIntersector();
111055           this._resultPrecisionModel = null;
111056           this._arg = null;
111057           if (arguments.length === 1) {
111058             var g0 = arguments[0];
111059             this.setComputationPrecision(g0.getPrecisionModel());
111060             this._arg = new Array(1).fill(null);
111061             this._arg[0] = new GeometryGraph(0, g0);
111062           } else if (arguments.length === 2) {
111063             var g0$1 = arguments[0];
111064             var g1 = arguments[1];
111065             var boundaryNodeRule = BoundaryNodeRule.OGC_SFS_BOUNDARY_RULE;
111066             if (g0$1.getPrecisionModel().compareTo(g1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$1.getPrecisionModel()); } else { this.setComputationPrecision(g1.getPrecisionModel()); }
111067             this._arg = new Array(2).fill(null);
111068             this._arg[0] = new GeometryGraph(0, g0$1, boundaryNodeRule);
111069             this._arg[1] = new GeometryGraph(1, g1, boundaryNodeRule);
111070           } else if (arguments.length === 3) {
111071             var g0$2 = arguments[0];
111072             var g1$1 = arguments[1];
111073             var boundaryNodeRule$1 = arguments[2];
111074             if (g0$2.getPrecisionModel().compareTo(g1$1.getPrecisionModel()) >= 0) { this.setComputationPrecision(g0$2.getPrecisionModel()); } else { this.setComputationPrecision(g1$1.getPrecisionModel()); }
111075             this._arg = new Array(2).fill(null);
111076             this._arg[0] = new GeometryGraph(0, g0$2, boundaryNodeRule$1);
111077             this._arg[1] = new GeometryGraph(1, g1$1, boundaryNodeRule$1);
111078           }
111079         };
111080         GeometryGraphOp.prototype.getArgGeometry = function getArgGeometry (i) {
111081           return this._arg[i].getGeometry()
111082         };
111083         GeometryGraphOp.prototype.setComputationPrecision = function setComputationPrecision (pm) {
111084           this._resultPrecisionModel = pm;
111085           this._li.setPrecisionModel(this._resultPrecisionModel);
111086         };
111087         GeometryGraphOp.prototype.interfaces_ = function interfaces_ () {
111088           return []
111089         };
111090         GeometryGraphOp.prototype.getClass = function getClass () {
111091           return GeometryGraphOp
111092         };
111093
111094         // operation.geometrygraph
111095
111096         var GeometryMapper = function GeometryMapper () {};
111097
111098         GeometryMapper.prototype.interfaces_ = function interfaces_ () {
111099           return []
111100         };
111101         GeometryMapper.prototype.getClass = function getClass () {
111102           return GeometryMapper
111103         };
111104         GeometryMapper.map = function map () {
111105           if (arguments[0] instanceof Geometry && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111106             var geom = arguments[0];
111107             var op = arguments[1];
111108             var mapped = new ArrayList();
111109             for (var i = 0; i < geom.getNumGeometries(); i++) {
111110               var g = op.map(geom.getGeometryN(i));
111111               if (g !== null) { mapped.add(g); }
111112             }
111113             return geom.getFactory().buildGeometry(mapped)
111114           } else if (hasInterface(arguments[0], Collection) && hasInterface(arguments[1], GeometryMapper.MapOp)) {
111115             var geoms = arguments[0];
111116             var op$1 = arguments[1];
111117             var mapped$1 = new ArrayList();
111118             for (var i$1 = geoms.iterator(); i$1.hasNext();) {
111119               var g$1 = i$1.next();
111120               var gr = op$1.map(g$1);
111121               if (gr !== null) { mapped$1.add(gr); }
111122             }
111123             return mapped$1
111124           }
111125         };
111126         GeometryMapper.MapOp = function MapOp () {};
111127
111128         var OverlayOp = (function (GeometryGraphOp) {
111129           function OverlayOp () {
111130             var g0 = arguments[0];
111131             var g1 = arguments[1];
111132             GeometryGraphOp.call(this, g0, g1);
111133             this._ptLocator = new PointLocator();
111134             this._geomFact = null;
111135             this._resultGeom = null;
111136             this._graph = null;
111137             this._edgeList = new EdgeList();
111138             this._resultPolyList = new ArrayList();
111139             this._resultLineList = new ArrayList();
111140             this._resultPointList = new ArrayList();
111141             this._graph = new PlanarGraph(new OverlayNodeFactory());
111142             this._geomFact = g0.getFactory();
111143           }
111144
111145           if ( GeometryGraphOp ) { OverlayOp.__proto__ = GeometryGraphOp; }
111146           OverlayOp.prototype = Object.create( GeometryGraphOp && GeometryGraphOp.prototype );
111147           OverlayOp.prototype.constructor = OverlayOp;
111148           OverlayOp.prototype.insertUniqueEdge = function insertUniqueEdge (e) {
111149             var existingEdge = this._edgeList.findEqualEdge(e);
111150             if (existingEdge !== null) {
111151               var existingLabel = existingEdge.getLabel();
111152               var labelToMerge = e.getLabel();
111153               if (!existingEdge.isPointwiseEqual(e)) {
111154                 labelToMerge = new Label(e.getLabel());
111155                 labelToMerge.flip();
111156               }
111157               var depth = existingEdge.getDepth();
111158               if (depth.isNull()) {
111159                 depth.add(existingLabel);
111160               }
111161               depth.add(labelToMerge);
111162               existingLabel.merge(labelToMerge);
111163             } else {
111164               this._edgeList.add(e);
111165             }
111166           };
111167           OverlayOp.prototype.getGraph = function getGraph () {
111168             return this._graph
111169           };
111170           OverlayOp.prototype.cancelDuplicateResultEdges = function cancelDuplicateResultEdges () {
111171             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111172               var de = it.next();
111173               var sym = de.getSym();
111174               if (de.isInResult() && sym.isInResult()) {
111175                 de.setInResult(false);
111176                 sym.setInResult(false);
111177               }
111178             }
111179           };
111180           OverlayOp.prototype.isCoveredByLA = function isCoveredByLA (coord) {
111181             if (this.isCovered(coord, this._resultLineList)) { return true }
111182             if (this.isCovered(coord, this._resultPolyList)) { return true }
111183             return false
111184           };
111185           OverlayOp.prototype.computeGeometry = function computeGeometry (resultPointList, resultLineList, resultPolyList, opcode) {
111186             var geomList = new ArrayList();
111187             geomList.addAll(resultPointList);
111188             geomList.addAll(resultLineList);
111189             geomList.addAll(resultPolyList);
111190             if (geomList.isEmpty()) { return OverlayOp.createEmptyResult(opcode, this._arg[0].getGeometry(), this._arg[1].getGeometry(), this._geomFact) }
111191             return this._geomFact.buildGeometry(geomList)
111192           };
111193           OverlayOp.prototype.mergeSymLabels = function mergeSymLabels () {
111194             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111195               var node = nodeit.next();
111196               node.getEdges().mergeSymLabels();
111197             }
111198           };
111199           OverlayOp.prototype.isCovered = function isCovered (coord, geomList) {
111200             var this$1 = this;
111201
111202             for (var it = geomList.iterator(); it.hasNext();) {
111203               var geom = it.next();
111204               var loc = this$1._ptLocator.locate(coord, geom);
111205               if (loc !== Location.EXTERIOR) { return true }
111206             }
111207             return false
111208           };
111209           OverlayOp.prototype.replaceCollapsedEdges = function replaceCollapsedEdges () {
111210             var newEdges = new ArrayList();
111211             for (var it = this._edgeList.iterator(); it.hasNext();) {
111212               var e = it.next();
111213               if (e.isCollapsed()) {
111214                 it.remove();
111215                 newEdges.add(e.getCollapsedEdge());
111216               }
111217             }
111218             this._edgeList.addAll(newEdges);
111219           };
111220           OverlayOp.prototype.updateNodeLabelling = function updateNodeLabelling () {
111221             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111222               var node = nodeit.next();
111223               var lbl = node.getEdges().getLabel();
111224               node.getLabel().merge(lbl);
111225             }
111226           };
111227           OverlayOp.prototype.getResultGeometry = function getResultGeometry (overlayOpCode) {
111228             this.computeOverlay(overlayOpCode);
111229             return this._resultGeom
111230           };
111231           OverlayOp.prototype.insertUniqueEdges = function insertUniqueEdges (edges) {
111232             var this$1 = this;
111233
111234             for (var i = edges.iterator(); i.hasNext();) {
111235               var e = i.next();
111236               this$1.insertUniqueEdge(e);
111237             }
111238           };
111239           OverlayOp.prototype.computeOverlay = function computeOverlay (opCode) {
111240             this.copyPoints(0);
111241             this.copyPoints(1);
111242             this._arg[0].computeSelfNodes(this._li, false);
111243             this._arg[1].computeSelfNodes(this._li, false);
111244             this._arg[0].computeEdgeIntersections(this._arg[1], this._li, true);
111245             var baseSplitEdges = new ArrayList();
111246             this._arg[0].computeSplitEdges(baseSplitEdges);
111247             this._arg[1].computeSplitEdges(baseSplitEdges);
111248             // const splitEdges = baseSplitEdges
111249             this.insertUniqueEdges(baseSplitEdges);
111250             this.computeLabelsFromDepths();
111251             this.replaceCollapsedEdges();
111252             EdgeNodingValidator.checkValid(this._edgeList.getEdges());
111253             this._graph.addEdges(this._edgeList.getEdges());
111254             this.computeLabelling();
111255             this.labelIncompleteNodes();
111256             this.findResultAreaEdges(opCode);
111257             this.cancelDuplicateResultEdges();
111258             var polyBuilder = new PolygonBuilder(this._geomFact);
111259             polyBuilder.add(this._graph);
111260             this._resultPolyList = polyBuilder.getPolygons();
111261             var lineBuilder = new LineBuilder(this, this._geomFact, this._ptLocator);
111262             this._resultLineList = lineBuilder.build(opCode);
111263             var pointBuilder = new PointBuilder(this, this._geomFact, this._ptLocator);
111264             this._resultPointList = pointBuilder.build(opCode);
111265             this._resultGeom = this.computeGeometry(this._resultPointList, this._resultLineList, this._resultPolyList, opCode);
111266           };
111267           OverlayOp.prototype.labelIncompleteNode = function labelIncompleteNode (n, targetIndex) {
111268             var loc = this._ptLocator.locate(n.getCoordinate(), this._arg[targetIndex].getGeometry());
111269             n.getLabel().setLocation(targetIndex, loc);
111270           };
111271           OverlayOp.prototype.copyPoints = function copyPoints (argIndex) {
111272             var this$1 = this;
111273
111274             for (var i = this._arg[argIndex].getNodeIterator(); i.hasNext();) {
111275               var graphNode = i.next();
111276               var newNode = this$1._graph.addNode(graphNode.getCoordinate());
111277               newNode.setLabel(argIndex, graphNode.getLabel().getLocation(argIndex));
111278             }
111279           };
111280           OverlayOp.prototype.findResultAreaEdges = function findResultAreaEdges (opCode) {
111281             for (var it = this._graph.getEdgeEnds().iterator(); it.hasNext();) {
111282               var de = it.next();
111283               var label = de.getLabel();
111284               if (label.isArea() && !de.isInteriorAreaEdge() && OverlayOp.isResultOfOp(label.getLocation(0, Position.RIGHT), label.getLocation(1, Position.RIGHT), opCode)) {
111285                 de.setInResult(true);
111286               }
111287             }
111288           };
111289           OverlayOp.prototype.computeLabelsFromDepths = function computeLabelsFromDepths () {
111290             for (var it = this._edgeList.iterator(); it.hasNext();) {
111291               var e = it.next();
111292               var lbl = e.getLabel();
111293               var depth = e.getDepth();
111294               if (!depth.isNull()) {
111295                 depth.normalize();
111296                 for (var i = 0; i < 2; i++) {
111297                   if (!lbl.isNull(i) && lbl.isArea() && !depth.isNull(i)) {
111298                     if (depth.getDelta(i) === 0) {
111299                       lbl.toLine(i);
111300                     } else {
111301                       Assert.isTrue(!depth.isNull(i, Position.LEFT), 'depth of LEFT side has not been initialized');
111302                       lbl.setLocation(i, Position.LEFT, depth.getLocation(i, Position.LEFT));
111303                       Assert.isTrue(!depth.isNull(i, Position.RIGHT), 'depth of RIGHT side has not been initialized');
111304                       lbl.setLocation(i, Position.RIGHT, depth.getLocation(i, Position.RIGHT));
111305                     }
111306                   }
111307                 }
111308               }
111309             }
111310           };
111311           OverlayOp.prototype.computeLabelling = function computeLabelling () {
111312             var this$1 = this;
111313
111314             for (var nodeit = this._graph.getNodes().iterator(); nodeit.hasNext();) {
111315               var node = nodeit.next();
111316               node.getEdges().computeLabelling(this$1._arg);
111317             }
111318             this.mergeSymLabels();
111319             this.updateNodeLabelling();
111320           };
111321           OverlayOp.prototype.labelIncompleteNodes = function labelIncompleteNodes () {
111322             var this$1 = this;
111323
111324             // let nodeCount = 0
111325             for (var ni = this._graph.getNodes().iterator(); ni.hasNext();) {
111326               var n = ni.next();
111327               var label = n.getLabel();
111328               if (n.isIsolated()) {
111329                 // nodeCount++
111330                 if (label.isNull(0)) { this$1.labelIncompleteNode(n, 0); } else { this$1.labelIncompleteNode(n, 1); }
111331               }
111332               n.getEdges().updateLabelling(label);
111333             }
111334           };
111335           OverlayOp.prototype.isCoveredByA = function isCoveredByA (coord) {
111336             if (this.isCovered(coord, this._resultPolyList)) { return true }
111337             return false
111338           };
111339           OverlayOp.prototype.interfaces_ = function interfaces_ () {
111340             return []
111341           };
111342           OverlayOp.prototype.getClass = function getClass () {
111343             return OverlayOp
111344           };
111345
111346           return OverlayOp;
111347         }(GeometryGraphOp));
111348
111349         OverlayOp.overlayOp = function (geom0, geom1, opCode) {
111350           var gov = new OverlayOp(geom0, geom1);
111351           var geomOv = gov.getResultGeometry(opCode);
111352           return geomOv
111353         };
111354         OverlayOp.intersection = function (g, other) {
111355           if (g.isEmpty() || other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.INTERSECTION, g, other, g.getFactory()) }
111356           if (g.isGeometryCollection()) {
111357             var g2 = other;
111358             return GeometryCollectionMapper.map(g, {
111359               interfaces_: function () {
111360                 return [GeometryMapper.MapOp]
111361               },
111362               map: function (g) {
111363                 return g.intersection(g2)
111364               }
111365             })
111366           }
111367           g.checkNotGeometryCollection(g);
111368           g.checkNotGeometryCollection(other);
111369           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.INTERSECTION)
111370         };
111371         OverlayOp.symDifference = function (g, other) {
111372           if (g.isEmpty() || other.isEmpty()) {
111373             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.SYMDIFFERENCE, g, other, g.getFactory()) }
111374             if (g.isEmpty()) { return other.copy() }
111375             if (other.isEmpty()) { return g.copy() }
111376           }
111377           g.checkNotGeometryCollection(g);
111378           g.checkNotGeometryCollection(other);
111379           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.SYMDIFFERENCE)
111380         };
111381         OverlayOp.resultDimension = function (opCode, g0, g1) {
111382           var dim0 = g0.getDimension();
111383           var dim1 = g1.getDimension();
111384           var resultDimension = -1;
111385           switch (opCode) {
111386             case OverlayOp.INTERSECTION:
111387               resultDimension = Math.min(dim0, dim1);
111388               break
111389             case OverlayOp.UNION:
111390               resultDimension = Math.max(dim0, dim1);
111391               break
111392             case OverlayOp.DIFFERENCE:
111393               resultDimension = dim0;
111394               break
111395             case OverlayOp.SYMDIFFERENCE:
111396               resultDimension = Math.max(dim0, dim1);
111397               break
111398           }
111399           return resultDimension
111400         };
111401         OverlayOp.createEmptyResult = function (overlayOpCode, a, b, geomFact) {
111402           var result = null;
111403           switch (OverlayOp.resultDimension(overlayOpCode, a, b)) {
111404             case -1:
111405               result = geomFact.createGeometryCollection(new Array(0).fill(null));
111406               break
111407             case 0:
111408               result = geomFact.createPoint();
111409               break
111410             case 1:
111411               result = geomFact.createLineString();
111412               break
111413             case 2:
111414               result = geomFact.createPolygon();
111415               break
111416           }
111417           return result
111418         };
111419         OverlayOp.difference = function (g, other) {
111420           if (g.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.DIFFERENCE, g, other, g.getFactory()) }
111421           if (other.isEmpty()) { return g.copy() }
111422           g.checkNotGeometryCollection(g);
111423           g.checkNotGeometryCollection(other);
111424           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.DIFFERENCE)
111425         };
111426         OverlayOp.isResultOfOp = function () {
111427           if (arguments.length === 2) {
111428             var label = arguments[0];
111429             var opCode = arguments[1];
111430             var loc0 = label.getLocation(0);
111431             var loc1 = label.getLocation(1);
111432             return OverlayOp.isResultOfOp(loc0, loc1, opCode)
111433           } else if (arguments.length === 3) {
111434             var loc0$1 = arguments[0];
111435             var loc1$1 = arguments[1];
111436             var overlayOpCode = arguments[2];
111437             if (loc0$1 === Location.BOUNDARY) { loc0$1 = Location.INTERIOR; }
111438             if (loc1$1 === Location.BOUNDARY) { loc1$1 = Location.INTERIOR; }
111439             switch (overlayOpCode) {
111440               case OverlayOp.INTERSECTION:
111441                 return loc0$1 === Location.INTERIOR && loc1$1 === Location.INTERIOR
111442               case OverlayOp.UNION:
111443                 return loc0$1 === Location.INTERIOR || loc1$1 === Location.INTERIOR
111444               case OverlayOp.DIFFERENCE:
111445                 return loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR
111446               case OverlayOp.SYMDIFFERENCE:
111447                 return (loc0$1 === Location.INTERIOR && loc1$1 !== Location.INTERIOR) || (loc0$1 !== Location.INTERIOR && loc1$1 === Location.INTERIOR)
111448             }
111449             return false
111450           }
111451         };
111452         OverlayOp.INTERSECTION = 1;
111453         OverlayOp.UNION = 2;
111454         OverlayOp.DIFFERENCE = 3;
111455         OverlayOp.SYMDIFFERENCE = 4;
111456
111457         var FuzzyPointLocator = function FuzzyPointLocator () {
111458           this._g = null;
111459           this._boundaryDistanceTolerance = null;
111460           this._linework = null;
111461           this._ptLocator = new PointLocator();
111462           this._seg = new LineSegment();
111463           var g = arguments[0];
111464           var boundaryDistanceTolerance = arguments[1];
111465           this._g = g;
111466           this._boundaryDistanceTolerance = boundaryDistanceTolerance;
111467           this._linework = this.extractLinework(g);
111468         };
111469         FuzzyPointLocator.prototype.isWithinToleranceOfBoundary = function isWithinToleranceOfBoundary (pt) {
111470             var this$1 = this;
111471
111472           for (var i = 0; i < this._linework.getNumGeometries(); i++) {
111473             var line = this$1._linework.getGeometryN(i);
111474             var seq = line.getCoordinateSequence();
111475             for (var j = 0; j < seq.size() - 1; j++) {
111476               seq.getCoordinate(j, this$1._seg.p0);
111477               seq.getCoordinate(j + 1, this$1._seg.p1);
111478               var dist = this$1._seg.distance(pt);
111479               if (dist <= this$1._boundaryDistanceTolerance) { return true }
111480             }
111481           }
111482           return false
111483         };
111484         FuzzyPointLocator.prototype.getLocation = function getLocation (pt) {
111485           if (this.isWithinToleranceOfBoundary(pt)) { return Location.BOUNDARY }
111486           return this._ptLocator.locate(pt, this._g)
111487         };
111488         FuzzyPointLocator.prototype.extractLinework = function extractLinework (g) {
111489           var extracter = new PolygonalLineworkExtracter();
111490           g.apply(extracter);
111491           var linework = extracter.getLinework();
111492           var lines = GeometryFactory.toLineStringArray(linework);
111493           return g.getFactory().createMultiLineString(lines)
111494         };
111495         FuzzyPointLocator.prototype.interfaces_ = function interfaces_ () {
111496           return []
111497         };
111498         FuzzyPointLocator.prototype.getClass = function getClass () {
111499           return FuzzyPointLocator
111500         };
111501
111502         var PolygonalLineworkExtracter = function PolygonalLineworkExtracter () {
111503           this._linework = null;
111504           this._linework = new ArrayList();
111505         };
111506         PolygonalLineworkExtracter.prototype.getLinework = function getLinework () {
111507           return this._linework
111508         };
111509         PolygonalLineworkExtracter.prototype.filter = function filter (g) {
111510             var this$1 = this;
111511
111512           if (g instanceof Polygon) {
111513             var poly = g;
111514             this._linework.add(poly.getExteriorRing());
111515             for (var i = 0; i < poly.getNumInteriorRing(); i++) {
111516               this$1._linework.add(poly.getInteriorRingN(i));
111517             }
111518           }
111519         };
111520         PolygonalLineworkExtracter.prototype.interfaces_ = function interfaces_ () {
111521           return [GeometryFilter]
111522         };
111523         PolygonalLineworkExtracter.prototype.getClass = function getClass () {
111524           return PolygonalLineworkExtracter
111525         };
111526
111527         var OffsetPointGenerator = function OffsetPointGenerator () {
111528           this._g = null;
111529           this._doLeft = true;
111530           this._doRight = true;
111531           var g = arguments[0];
111532           this._g = g;
111533         };
111534         OffsetPointGenerator.prototype.extractPoints = function extractPoints (line, offsetDistance, offsetPts) {
111535             var this$1 = this;
111536
111537           var pts = line.getCoordinates();
111538           for (var i = 0; i < pts.length - 1; i++) {
111539             this$1.computeOffsetPoints(pts[i], pts[i + 1], offsetDistance, offsetPts);
111540           }
111541         };
111542         OffsetPointGenerator.prototype.setSidesToGenerate = function setSidesToGenerate (doLeft, doRight) {
111543           this._doLeft = doLeft;
111544           this._doRight = doRight;
111545         };
111546         OffsetPointGenerator.prototype.getPoints = function getPoints (offsetDistance) {
111547             var this$1 = this;
111548
111549           var offsetPts = new ArrayList();
111550           var lines = LinearComponentExtracter.getLines(this._g);
111551           for (var i = lines.iterator(); i.hasNext();) {
111552             var line = i.next();
111553             this$1.extractPoints(line, offsetDistance, offsetPts);
111554           }
111555           return offsetPts
111556         };
111557         OffsetPointGenerator.prototype.computeOffsetPoints = function computeOffsetPoints (p0, p1, offsetDistance, offsetPts) {
111558           var dx = p1.x - p0.x;
111559           var dy = p1.y - p0.y;
111560           var len = Math.sqrt(dx * dx + dy * dy);
111561           var ux = offsetDistance * dx / len;
111562           var uy = offsetDistance * dy / len;
111563           var midX = (p1.x + p0.x) / 2;
111564           var midY = (p1.y + p0.y) / 2;
111565           if (this._doLeft) {
111566             var offsetLeft = new Coordinate(midX - uy, midY + ux);
111567             offsetPts.add(offsetLeft);
111568           }
111569           if (this._doRight) {
111570             var offsetRight = new Coordinate(midX + uy, midY - ux);
111571             offsetPts.add(offsetRight);
111572           }
111573         };
111574         OffsetPointGenerator.prototype.interfaces_ = function interfaces_ () {
111575           return []
111576         };
111577         OffsetPointGenerator.prototype.getClass = function getClass () {
111578           return OffsetPointGenerator
111579         };
111580
111581         var OverlayResultValidator = function OverlayResultValidator () {
111582           this._geom = null;
111583           this._locFinder = null;
111584           this._location = new Array(3).fill(null);
111585           this._invalidLocation = null;
111586           this._boundaryDistanceTolerance = OverlayResultValidator.TOLERANCE;
111587           this._testCoords = new ArrayList();
111588           var a = arguments[0];
111589           var b = arguments[1];
111590           var result = arguments[2];
111591           this._boundaryDistanceTolerance = OverlayResultValidator.computeBoundaryDistanceTolerance(a, b);
111592           this._geom = [a, b, result];
111593           this._locFinder = [new FuzzyPointLocator(this._geom[0], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[1], this._boundaryDistanceTolerance), new FuzzyPointLocator(this._geom[2], this._boundaryDistanceTolerance)];
111594         };
111595
111596         var staticAccessors$46 = { TOLERANCE: { configurable: true } };
111597         OverlayResultValidator.prototype.reportResult = function reportResult (overlayOp, location, expectedInterior) {
111598           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]));
111599         };
111600         OverlayResultValidator.prototype.isValid = function isValid (overlayOp) {
111601           this.addTestPts(this._geom[0]);
111602           this.addTestPts(this._geom[1]);
111603           var isValid = this.checkValid(overlayOp);
111604           return isValid
111605         };
111606         OverlayResultValidator.prototype.checkValid = function checkValid () {
111607             var this$1 = this;
111608
111609           if (arguments.length === 1) {
111610             var overlayOp = arguments[0];
111611             for (var i = 0; i < this._testCoords.size(); i++) {
111612               var pt = this$1._testCoords.get(i);
111613               if (!this$1.checkValid(overlayOp, pt)) {
111614                 this$1._invalidLocation = pt;
111615                 return false
111616               }
111617             }
111618             return true
111619           } else if (arguments.length === 2) {
111620             var overlayOp$1 = arguments[0];
111621             var pt$1 = arguments[1];
111622             this._location[0] = this._locFinder[0].getLocation(pt$1);
111623             this._location[1] = this._locFinder[1].getLocation(pt$1);
111624             this._location[2] = this._locFinder[2].getLocation(pt$1);
111625             if (OverlayResultValidator.hasLocation(this._location, Location.BOUNDARY)) { return true }
111626             return this.isValidResult(overlayOp$1, this._location)
111627           }
111628         };
111629         OverlayResultValidator.prototype.addTestPts = function addTestPts (g) {
111630           var ptGen = new OffsetPointGenerator(g);
111631           this._testCoords.addAll(ptGen.getPoints(5 * this._boundaryDistanceTolerance));
111632         };
111633         OverlayResultValidator.prototype.isValidResult = function isValidResult (overlayOp, location) {
111634           var expectedInterior = OverlayOp.isResultOfOp(location[0], location[1], overlayOp);
111635           var resultInInterior = location[2] === Location.INTERIOR;
111636           var isValid = !(expectedInterior ^ resultInInterior);
111637           if (!isValid) { this.reportResult(overlayOp, location, expectedInterior); }
111638           return isValid
111639         };
111640         OverlayResultValidator.prototype.getInvalidLocation = function getInvalidLocation () {
111641           return this._invalidLocation
111642         };
111643         OverlayResultValidator.prototype.interfaces_ = function interfaces_ () {
111644           return []
111645         };
111646         OverlayResultValidator.prototype.getClass = function getClass () {
111647           return OverlayResultValidator
111648         };
111649         OverlayResultValidator.hasLocation = function hasLocation (location, loc) {
111650           for (var i = 0; i < 3; i++) {
111651             if (location[i] === loc) { return true }
111652           }
111653           return false
111654         };
111655         OverlayResultValidator.computeBoundaryDistanceTolerance = function computeBoundaryDistanceTolerance (g0, g1) {
111656           return Math.min(GeometrySnapper.computeSizeBasedSnapTolerance(g0), GeometrySnapper.computeSizeBasedSnapTolerance(g1))
111657         };
111658         OverlayResultValidator.isValid = function isValid (a, b, overlayOp, result) {
111659           var validator = new OverlayResultValidator(a, b, result);
111660           return validator.isValid(overlayOp)
111661         };
111662         staticAccessors$46.TOLERANCE.get = function () { return 0.000001 };
111663
111664         Object.defineProperties( OverlayResultValidator, staticAccessors$46 );
111665
111666         // operation.overlay
111667
111668         var GeometryCombiner = function GeometryCombiner (geoms) {
111669           this._geomFactory = null;
111670           this._skipEmpty = false;
111671           this._inputGeoms = null;
111672           this._geomFactory = GeometryCombiner.extractFactory(geoms);
111673           this._inputGeoms = geoms;
111674         };
111675         GeometryCombiner.prototype.extractElements = function extractElements (geom, elems) {
111676             var this$1 = this;
111677
111678           if (geom === null) { return null }
111679           for (var i = 0; i < geom.getNumGeometries(); i++) {
111680             var elemGeom = geom.getGeometryN(i);
111681             if (this$1._skipEmpty && elemGeom.isEmpty()) { continue }
111682             elems.add(elemGeom);
111683           }
111684         };
111685         GeometryCombiner.prototype.combine = function combine () {
111686             var this$1 = this;
111687
111688           var elems = new ArrayList();
111689           for (var i = this._inputGeoms.iterator(); i.hasNext();) {
111690             var g = i.next();
111691             this$1.extractElements(g, elems);
111692           }
111693           if (elems.size() === 0) {
111694             if (this._geomFactory !== null) {
111695               return this._geomFactory.createGeometryCollection(null)
111696             }
111697             return null
111698           }
111699           return this._geomFactory.buildGeometry(elems)
111700         };
111701         GeometryCombiner.prototype.interfaces_ = function interfaces_ () {
111702           return []
111703         };
111704         GeometryCombiner.prototype.getClass = function getClass () {
111705           return GeometryCombiner
111706         };
111707         GeometryCombiner.combine = function combine () {
111708           if (arguments.length === 1) {
111709             var geoms = arguments[0];
111710             var combiner = new GeometryCombiner(geoms);
111711             return combiner.combine()
111712           } else if (arguments.length === 2) {
111713             var g0 = arguments[0];
111714             var g1 = arguments[1];
111715             var combiner$1 = new GeometryCombiner(GeometryCombiner.createList(g0, g1));
111716             return combiner$1.combine()
111717           } else if (arguments.length === 3) {
111718             var g0$1 = arguments[0];
111719             var g1$1 = arguments[1];
111720             var g2 = arguments[2];
111721             var combiner$2 = new GeometryCombiner(GeometryCombiner.createList(g0$1, g1$1, g2));
111722             return combiner$2.combine()
111723           }
111724         };
111725         GeometryCombiner.extractFactory = function extractFactory (geoms) {
111726           if (geoms.isEmpty()) { return null }
111727           return geoms.iterator().next().getFactory()
111728         };
111729         GeometryCombiner.createList = function createList () {
111730           if (arguments.length === 2) {
111731             var obj0 = arguments[0];
111732             var obj1 = arguments[1];
111733             var list = new ArrayList();
111734             list.add(obj0);
111735             list.add(obj1);
111736             return list
111737           } else if (arguments.length === 3) {
111738             var obj0$1 = arguments[0];
111739             var obj1$1 = arguments[1];
111740             var obj2 = arguments[2];
111741             var list$1 = new ArrayList();
111742             list$1.add(obj0$1);
111743             list$1.add(obj1$1);
111744             list$1.add(obj2);
111745             return list$1
111746           }
111747         };
111748
111749         var CascadedPolygonUnion = function CascadedPolygonUnion () {
111750           this._inputPolys = null;
111751           this._geomFactory = null;
111752           var polys = arguments[0];
111753           this._inputPolys = polys;
111754           if (this._inputPolys === null) { this._inputPolys = new ArrayList(); }
111755         };
111756
111757         var staticAccessors$47 = { STRTREE_NODE_CAPACITY: { configurable: true } };
111758         CascadedPolygonUnion.prototype.reduceToGeometries = function reduceToGeometries (geomTree) {
111759             var this$1 = this;
111760
111761           var geoms = new ArrayList();
111762           for (var i = geomTree.iterator(); i.hasNext();) {
111763             var o = i.next();
111764             var geom = null;
111765             if (hasInterface(o, List)) {
111766               geom = this$1.unionTree(o);
111767             } else if (o instanceof Geometry) {
111768               geom = o;
111769             }
111770             geoms.add(geom);
111771           }
111772           return geoms
111773         };
111774         CascadedPolygonUnion.prototype.extractByEnvelope = function extractByEnvelope (env, geom, disjointGeoms) {
111775           var intersectingGeoms = new ArrayList();
111776           for (var i = 0; i < geom.getNumGeometries(); i++) {
111777             var elem = geom.getGeometryN(i);
111778             if (elem.getEnvelopeInternal().intersects(env)) { intersectingGeoms.add(elem); } else { disjointGeoms.add(elem); }
111779           }
111780           return this._geomFactory.buildGeometry(intersectingGeoms)
111781         };
111782         CascadedPolygonUnion.prototype.unionOptimized = function unionOptimized (g0, g1) {
111783           var g0Env = g0.getEnvelopeInternal();
111784           var g1Env = g1.getEnvelopeInternal();
111785           if (!g0Env.intersects(g1Env)) {
111786             var combo = GeometryCombiner.combine(g0, g1);
111787             return combo
111788           }
111789           if (g0.getNumGeometries() <= 1 && g1.getNumGeometries() <= 1) { return this.unionActual(g0, g1) }
111790           var commonEnv = g0Env.intersection(g1Env);
111791           return this.unionUsingEnvelopeIntersection(g0, g1, commonEnv)
111792         };
111793         CascadedPolygonUnion.prototype.union = function union () {
111794           if (this._inputPolys === null) { throw new Error('union() method cannot be called twice') }
111795           if (this._inputPolys.isEmpty()) { return null }
111796           this._geomFactory = this._inputPolys.iterator().next().getFactory();
111797           var index = new STRtree(CascadedPolygonUnion.STRTREE_NODE_CAPACITY);
111798           for (var i = this._inputPolys.iterator(); i.hasNext();) {
111799             var item = i.next();
111800             index.insert(item.getEnvelopeInternal(), item);
111801           }
111802           this._inputPolys = null;
111803           var itemTree = index.itemsTree();
111804           var unionAll = this.unionTree(itemTree);
111805           return unionAll
111806         };
111807         CascadedPolygonUnion.prototype.binaryUnion = function binaryUnion () {
111808           if (arguments.length === 1) {
111809             var geoms = arguments[0];
111810             return this.binaryUnion(geoms, 0, geoms.size())
111811           } else if (arguments.length === 3) {
111812             var geoms$1 = arguments[0];
111813             var start = arguments[1];
111814             var end = arguments[2];
111815             if (end - start <= 1) {
111816               var g0 = CascadedPolygonUnion.getGeometry(geoms$1, start);
111817               return this.unionSafe(g0, null)
111818             } else if (end - start === 2) {
111819               return this.unionSafe(CascadedPolygonUnion.getGeometry(geoms$1, start), CascadedPolygonUnion.getGeometry(geoms$1, start + 1))
111820             } else {
111821               var mid = Math.trunc((end + start) / 2);
111822               var g0$1 = this.binaryUnion(geoms$1, start, mid);
111823               var g1 = this.binaryUnion(geoms$1, mid, end);
111824               return this.unionSafe(g0$1, g1)
111825             }
111826           }
111827         };
111828         CascadedPolygonUnion.prototype.repeatedUnion = function repeatedUnion (geoms) {
111829           var union = null;
111830           for (var i = geoms.iterator(); i.hasNext();) {
111831             var g = i.next();
111832             if (union === null) { union = g.copy(); } else { union = union.union(g); }
111833           }
111834           return union
111835         };
111836         CascadedPolygonUnion.prototype.unionSafe = function unionSafe (g0, g1) {
111837           if (g0 === null && g1 === null) { return null }
111838           if (g0 === null) { return g1.copy() }
111839           if (g1 === null) { return g0.copy() }
111840           return this.unionOptimized(g0, g1)
111841         };
111842         CascadedPolygonUnion.prototype.unionActual = function unionActual (g0, g1) {
111843           return CascadedPolygonUnion.restrictToPolygons(g0.union(g1))
111844         };
111845         CascadedPolygonUnion.prototype.unionTree = function unionTree (geomTree) {
111846           var geoms = this.reduceToGeometries(geomTree);
111847           var union = this.binaryUnion(geoms);
111848           return union
111849         };
111850         CascadedPolygonUnion.prototype.unionUsingEnvelopeIntersection = function unionUsingEnvelopeIntersection (g0, g1, common) {
111851           var disjointPolys = new ArrayList();
111852           var g0Int = this.extractByEnvelope(common, g0, disjointPolys);
111853           var g1Int = this.extractByEnvelope(common, g1, disjointPolys);
111854           var union = this.unionActual(g0Int, g1Int);
111855           disjointPolys.add(union);
111856           var overallUnion = GeometryCombiner.combine(disjointPolys);
111857           return overallUnion
111858         };
111859         CascadedPolygonUnion.prototype.bufferUnion = function bufferUnion () {
111860           if (arguments.length === 1) {
111861             var geoms = arguments[0];
111862             var factory = geoms.get(0).getFactory();
111863             var gColl = factory.buildGeometry(geoms);
111864             var unionAll = gColl.buffer(0.0);
111865             return unionAll
111866           } else if (arguments.length === 2) {
111867             var g0 = arguments[0];
111868             var g1 = arguments[1];
111869             var factory$1 = g0.getFactory();
111870             var gColl$1 = factory$1.createGeometryCollection([g0, g1]);
111871             var unionAll$1 = gColl$1.buffer(0.0);
111872             return unionAll$1
111873           }
111874         };
111875         CascadedPolygonUnion.prototype.interfaces_ = function interfaces_ () {
111876           return []
111877         };
111878         CascadedPolygonUnion.prototype.getClass = function getClass () {
111879           return CascadedPolygonUnion
111880         };
111881         CascadedPolygonUnion.restrictToPolygons = function restrictToPolygons (g) {
111882           if (hasInterface(g, Polygonal)) {
111883             return g
111884           }
111885           var polygons = PolygonExtracter.getPolygons(g);
111886           if (polygons.size() === 1) { return polygons.get(0) }
111887           return g.getFactory().createMultiPolygon(GeometryFactory.toPolygonArray(polygons))
111888         };
111889         CascadedPolygonUnion.getGeometry = function getGeometry (list, index) {
111890           if (index >= list.size()) { return null }
111891           return list.get(index)
111892         };
111893         CascadedPolygonUnion.union = function union (polys) {
111894           var op = new CascadedPolygonUnion(polys);
111895           return op.union()
111896         };
111897         staticAccessors$47.STRTREE_NODE_CAPACITY.get = function () { return 4 };
111898
111899         Object.defineProperties( CascadedPolygonUnion, staticAccessors$47 );
111900
111901         var UnionOp = function UnionOp () {};
111902
111903         UnionOp.prototype.interfaces_ = function interfaces_ () {
111904           return []
111905         };
111906         UnionOp.prototype.getClass = function getClass () {
111907           return UnionOp
111908         };
111909         UnionOp.union = function union (g, other) {
111910           if (g.isEmpty() || other.isEmpty()) {
111911             if (g.isEmpty() && other.isEmpty()) { return OverlayOp.createEmptyResult(OverlayOp.UNION, g, other, g.getFactory()) }
111912             if (g.isEmpty()) { return other.copy() }
111913             if (other.isEmpty()) { return g.copy() }
111914           }
111915           g.checkNotGeometryCollection(g);
111916           g.checkNotGeometryCollection(other);
111917           return SnapIfNeededOverlayOp.overlayOp(g, other, OverlayOp.UNION)
111918         };
111919
111920         /**
111921          * Earth Radius used with the Harvesine formula and approximates using a spherical (non-ellipsoid) Earth.
111922          */
111923
111924         /**
111925          * Wraps a GeoJSON {@link Geometry} in a GeoJSON {@link Feature}.
111926          *
111927          * @name feature
111928          * @param {Geometry} geometry input geometry
111929          * @param {Object} [properties={}] an Object of key-value pairs to add as properties
111930          * @param {Object} [options={}] Optional Parameters
111931          * @param {Array<number>} [options.bbox] Bounding Box Array [west, south, east, north] associated with the Feature
111932          * @param {string|number} [options.id] Identifier associated with the Feature
111933          * @returns {Feature} a GeoJSON Feature
111934          * @example
111935          * var geometry = {
111936          *   "type": "Point",
111937          *   "coordinates": [110, 50]
111938          * };
111939          *
111940          * var feature = turf.feature(geometry);
111941          *
111942          * //=feature
111943          */
111944         function feature$1(geometry, properties, options) {
111945             // Optional Parameters
111946             options = options || {};
111947             if (!isObject$4(options)) { throw new Error('options is invalid'); }
111948             var bbox = options.bbox;
111949             var id = options.id;
111950
111951             // Validation
111952             if (geometry === undefined) { throw new Error('geometry is required'); }
111953             if (properties && properties.constructor !== Object) { throw new Error('properties must be an Object'); }
111954             if (bbox) { validateBBox(bbox); }
111955             if (id) { validateId(id); }
111956
111957             // Main
111958             var feat = {type: 'Feature'};
111959             if (id) { feat.id = id; }
111960             if (bbox) { feat.bbox = bbox; }
111961             feat.properties = properties || {};
111962             feat.geometry = geometry;
111963             return feat;
111964         }
111965
111966         /**
111967          * isNumber
111968          *
111969          * @param {*} num Number to validate
111970          * @returns {boolean} true/false
111971          * @example
111972          * turf.isNumber(123)
111973          * //=true
111974          * turf.isNumber('foo')
111975          * //=false
111976          */
111977         function isNumber$1(num) {
111978             return !isNaN(num) && num !== null && !Array.isArray(num);
111979         }
111980
111981         /**
111982          * isObject
111983          *
111984          * @param {*} input variable to validate
111985          * @returns {boolean} true/false
111986          * @example
111987          * turf.isObject({elevation: 10})
111988          * //=true
111989          * turf.isObject('foo')
111990          * //=false
111991          */
111992         function isObject$4(input) {
111993             return (!!input) && (input.constructor === Object);
111994         }
111995
111996         /**
111997          * Validate BBox
111998          *
111999          * @private
112000          * @param {Array<number>} bbox BBox to validate
112001          * @returns {void}
112002          * @throws Error if BBox is not valid
112003          * @example
112004          * validateBBox([-180, -40, 110, 50])
112005          * //=OK
112006          * validateBBox([-180, -40])
112007          * //=Error
112008          * validateBBox('Foo')
112009          * //=Error
112010          * validateBBox(5)
112011          * //=Error
112012          * validateBBox(null)
112013          * //=Error
112014          * validateBBox(undefined)
112015          * //=Error
112016          */
112017         function validateBBox(bbox) {
112018             if (!bbox) { throw new Error('bbox is required'); }
112019             if (!Array.isArray(bbox)) { throw new Error('bbox must be an Array'); }
112020             if (bbox.length !== 4 && bbox.length !== 6) { throw new Error('bbox must be an Array of 4 or 6 numbers'); }
112021             bbox.forEach(function (num) {
112022                 if (!isNumber$1(num)) { throw new Error('bbox must only contain numbers'); }
112023             });
112024         }
112025
112026         /**
112027          * Validate Id
112028          *
112029          * @private
112030          * @param {string|number} id Id to validate
112031          * @returns {void}
112032          * @throws Error if Id is not valid
112033          * @example
112034          * validateId([-180, -40, 110, 50])
112035          * //=Error
112036          * validateId([-180, -40])
112037          * //=Error
112038          * validateId('Foo')
112039          * //=OK
112040          * validateId(5)
112041          * //=OK
112042          * validateId(null)
112043          * //=Error
112044          * validateId(undefined)
112045          * //=Error
112046          */
112047         function validateId(id) {
112048             if (!id) { throw new Error('id is required'); }
112049             if (['string', 'number'].indexOf(typeof id) === -1) { throw new Error('id must be a number or a string'); }
112050         }
112051
112052         /**
112053          * Callback for geomEach
112054          *
112055          * @callback geomEachCallback
112056          * @param {Geometry} currentGeometry The current Geometry being processed.
112057          * @param {number} featureIndex The current index of the Feature being processed.
112058          * @param {Object} featureProperties The current Feature Properties being processed.
112059          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112060          * @param {number|string} featureId The current Feature Id being processed.
112061          */
112062
112063         /**
112064          * Iterate over each geometry in any GeoJSON object, similar to Array.forEach()
112065          *
112066          * @name geomEach
112067          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112068          * @param {Function} callback a method that takes (currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112069          * @returns {void}
112070          * @example
112071          * var features = turf.featureCollection([
112072          *     turf.point([26, 37], {foo: 'bar'}),
112073          *     turf.point([36, 53], {hello: 'world'})
112074          * ]);
112075          *
112076          * turf.geomEach(features, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112077          *   //=currentGeometry
112078          *   //=featureIndex
112079          *   //=featureProperties
112080          *   //=featureBBox
112081          *   //=featureId
112082          * });
112083          */
112084         function geomEach(geojson, callback) {
112085             var i, j, g, geometry, stopG,
112086                 geometryMaybeCollection,
112087                 isGeometryCollection,
112088                 featureProperties,
112089                 featureBBox,
112090                 featureId,
112091                 featureIndex = 0,
112092                 isFeatureCollection = geojson.type === 'FeatureCollection',
112093                 isFeature = geojson.type === 'Feature',
112094                 stop = isFeatureCollection ? geojson.features.length : 1;
112095
112096             // This logic may look a little weird. The reason why it is that way
112097             // is because it's trying to be fast. GeoJSON supports multiple kinds
112098             // of objects at its root: FeatureCollection, Features, Geometries.
112099             // This function has the responsibility of handling all of them, and that
112100             // means that some of the `for` loops you see below actually just don't apply
112101             // to certain inputs. For instance, if you give this just a
112102             // Point geometry, then both loops are short-circuited and all we do
112103             // is gradually rename the input until it's called 'geometry'.
112104             //
112105             // This also aims to allocate as few resources as possible: just a
112106             // few numbers and booleans, rather than any temporary arrays as would
112107             // be required with the normalization approach.
112108             for (i = 0; i < stop; i++) {
112109
112110                 geometryMaybeCollection = (isFeatureCollection ? geojson.features[i].geometry :
112111                     (isFeature ? geojson.geometry : geojson));
112112                 featureProperties = (isFeatureCollection ? geojson.features[i].properties :
112113                     (isFeature ? geojson.properties : {}));
112114                 featureBBox = (isFeatureCollection ? geojson.features[i].bbox :
112115                     (isFeature ? geojson.bbox : undefined));
112116                 featureId = (isFeatureCollection ? geojson.features[i].id :
112117                     (isFeature ? geojson.id : undefined));
112118                 isGeometryCollection = (geometryMaybeCollection) ? geometryMaybeCollection.type === 'GeometryCollection' : false;
112119                 stopG = isGeometryCollection ? geometryMaybeCollection.geometries.length : 1;
112120
112121                 for (g = 0; g < stopG; g++) {
112122                     geometry = isGeometryCollection ?
112123                         geometryMaybeCollection.geometries[g] : geometryMaybeCollection;
112124
112125                     // Handle null Geometry
112126                     if (geometry === null) {
112127                         if (callback(null, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112128                         continue;
112129                     }
112130                     switch (geometry.type) {
112131                     case 'Point':
112132                     case 'LineString':
112133                     case 'MultiPoint':
112134                     case 'Polygon':
112135                     case 'MultiLineString':
112136                     case 'MultiPolygon': {
112137                         if (callback(geometry, featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112138                         break;
112139                     }
112140                     case 'GeometryCollection': {
112141                         for (j = 0; j < geometry.geometries.length; j++) {
112142                             if (callback(geometry.geometries[j], featureIndex, featureProperties, featureBBox, featureId) === false) { return false; }
112143                         }
112144                         break;
112145                     }
112146                     default:
112147                         throw new Error('Unknown Geometry Type');
112148                     }
112149                 }
112150                 // Only increase `featureIndex` per each feature
112151                 featureIndex++;
112152             }
112153         }
112154
112155         /**
112156          * Callback for geomReduce
112157          *
112158          * The first time the callback function is called, the values provided as arguments depend
112159          * on whether the reduce method has an initialValue argument.
112160          *
112161          * If an initialValue is provided to the reduce method:
112162          *  - The previousValue argument is initialValue.
112163          *  - The currentValue argument is the value of the first element present in the array.
112164          *
112165          * If an initialValue is not provided:
112166          *  - The previousValue argument is the value of the first element present in the array.
112167          *  - The currentValue argument is the value of the second element present in the array.
112168          *
112169          * @callback geomReduceCallback
112170          * @param {*} previousValue The accumulated value previously returned in the last invocation
112171          * of the callback, or initialValue, if supplied.
112172          * @param {Geometry} currentGeometry The current Geometry being processed.
112173          * @param {number} featureIndex The current index of the Feature being processed.
112174          * @param {Object} featureProperties The current Feature Properties being processed.
112175          * @param {Array<number>} featureBBox The current Feature BBox being processed.
112176          * @param {number|string} featureId The current Feature Id being processed.
112177          */
112178
112179         /**
112180          * Reduce geometry in any GeoJSON object, similar to Array.reduce().
112181          *
112182          * @name geomReduce
112183          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112184          * @param {Function} callback a method that takes (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId)
112185          * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
112186          * @returns {*} The value that results from the reduction.
112187          * @example
112188          * var features = turf.featureCollection([
112189          *     turf.point([26, 37], {foo: 'bar'}),
112190          *     turf.point([36, 53], {hello: 'world'})
112191          * ]);
112192          *
112193          * turf.geomReduce(features, function (previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112194          *   //=previousValue
112195          *   //=currentGeometry
112196          *   //=featureIndex
112197          *   //=featureProperties
112198          *   //=featureBBox
112199          *   //=featureId
112200          *   return currentGeometry
112201          * });
112202          */
112203         function geomReduce(geojson, callback, initialValue) {
112204             var previousValue = initialValue;
112205             geomEach(geojson, function (currentGeometry, featureIndex, featureProperties, featureBBox, featureId) {
112206                 if (featureIndex === 0 && initialValue === undefined) { previousValue = currentGeometry; }
112207                 else { previousValue = callback(previousValue, currentGeometry, featureIndex, featureProperties, featureBBox, featureId); }
112208             });
112209             return previousValue;
112210         }
112211
112212         /**
112213          * Callback for flattenEach
112214          *
112215          * @callback flattenEachCallback
112216          * @param {Feature} currentFeature The current flattened feature being processed.
112217          * @param {number} featureIndex The current index of the Feature being processed.
112218          * @param {number} multiFeatureIndex The current index of the Multi-Feature being processed.
112219          */
112220
112221         /**
112222          * Iterate over flattened features in any GeoJSON object, similar to
112223          * Array.forEach.
112224          *
112225          * @name flattenEach
112226          * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON object
112227          * @param {Function} callback a method that takes (currentFeature, featureIndex, multiFeatureIndex)
112228          * @example
112229          * var features = turf.featureCollection([
112230          *     turf.point([26, 37], {foo: 'bar'}),
112231          *     turf.multiPoint([[40, 30], [36, 53]], {hello: 'world'})
112232          * ]);
112233          *
112234          * turf.flattenEach(features, function (currentFeature, featureIndex, multiFeatureIndex) {
112235          *   //=currentFeature
112236          *   //=featureIndex
112237          *   //=multiFeatureIndex
112238          * });
112239          */
112240         function flattenEach(geojson, callback) {
112241             geomEach(geojson, function (geometry, featureIndex, properties, bbox, id) {
112242                 // Callback for single geometry
112243                 var type = (geometry === null) ? null : geometry.type;
112244                 switch (type) {
112245                 case null:
112246                 case 'Point':
112247                 case 'LineString':
112248                 case 'Polygon':
112249                     if (callback(feature$1(geometry, properties, {bbox: bbox, id: id}), featureIndex, 0) === false) { return false; }
112250                     return;
112251                 }
112252
112253                 var geomType;
112254
112255                 // Callback for multi-geometry
112256                 switch (type) {
112257                 case 'MultiPoint':
112258                     geomType = 'Point';
112259                     break;
112260                 case 'MultiLineString':
112261                     geomType = 'LineString';
112262                     break;
112263                 case 'MultiPolygon':
112264                     geomType = 'Polygon';
112265                     break;
112266                 }
112267
112268                 for (var multiFeatureIndex = 0; multiFeatureIndex < geometry.coordinates.length; multiFeatureIndex++) {
112269                     var coordinate = geometry.coordinates[multiFeatureIndex];
112270                     var geom = {
112271                         type: geomType,
112272                         coordinates: coordinate
112273                     };
112274                     if (callback(feature$1(geom, properties), featureIndex, multiFeatureIndex) === false) { return false; }
112275                 }
112276             });
112277         }
112278
112279         /**
112280          * Takes one or more features and returns their area in square meters.
112281          *
112282          * @name area
112283          * @param {GeoJSON} geojson input GeoJSON feature(s)
112284          * @returns {number} area in square meters
112285          * @example
112286          * var polygon = turf.polygon([[[125, -15], [113, -22], [154, -27], [144, -15], [125, -15]]]);
112287          *
112288          * var area = turf.area(polygon);
112289          *
112290          * //addToMap
112291          * var addToMap = [polygon]
112292          * polygon.properties.area = area
112293          */
112294         function area(geojson) {
112295             return geomReduce(geojson, function (value, geom) {
112296                 return value + calculateArea(geom);
112297             }, 0);
112298         }
112299
112300         var RADIUS$1 = 6378137;
112301         // var FLATTENING_DENOM = 298.257223563;
112302         // var FLATTENING = 1 / FLATTENING_DENOM;
112303         // var POLAR_RADIUS = RADIUS * (1 - FLATTENING);
112304
112305         /**
112306          * Calculate Area
112307          *
112308          * @private
112309          * @param {GeoJSON} geojson GeoJSON
112310          * @returns {number} area
112311          */
112312         function calculateArea(geojson) {
112313             var area = 0, i;
112314             switch (geojson.type) {
112315             case 'Polygon':
112316                 return polygonArea$1(geojson.coordinates);
112317             case 'MultiPolygon':
112318                 for (i = 0; i < geojson.coordinates.length; i++) {
112319                     area += polygonArea$1(geojson.coordinates[i]);
112320                 }
112321                 return area;
112322             case 'Point':
112323             case 'MultiPoint':
112324             case 'LineString':
112325             case 'MultiLineString':
112326                 return 0;
112327             case 'GeometryCollection':
112328                 for (i = 0; i < geojson.geometries.length; i++) {
112329                     area += calculateArea(geojson.geometries[i]);
112330                 }
112331                 return area;
112332             }
112333         }
112334
112335         function polygonArea$1(coords) {
112336             var area = 0;
112337             if (coords && coords.length > 0) {
112338                 area += Math.abs(ringArea$1(coords[0]));
112339                 for (var i = 1; i < coords.length; i++) {
112340                     area -= Math.abs(ringArea$1(coords[i]));
112341                 }
112342             }
112343             return area;
112344         }
112345
112346         /**
112347          * @private
112348          * Calculate the approximate area of the polygon were it projected onto the earth.
112349          * Note that this area will be positive if ring is oriented clockwise, otherwise it will be negative.
112350          *
112351          * Reference:
112352          * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
112353          * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
112354          *
112355          * @param {Array<Array<number>>} coords Ring Coordinates
112356          * @returns {number} The approximate signed geodesic area of the polygon in square meters.
112357          */
112358         function ringArea$1(coords) {
112359             var p1;
112360             var p2;
112361             var p3;
112362             var lowerIndex;
112363             var middleIndex;
112364             var upperIndex;
112365             var i;
112366             var area = 0;
112367             var coordsLength = coords.length;
112368
112369             if (coordsLength > 2) {
112370                 for (i = 0; i < coordsLength; i++) {
112371                     if (i === coordsLength - 2) { // i = N-2
112372                         lowerIndex = coordsLength - 2;
112373                         middleIndex = coordsLength - 1;
112374                         upperIndex = 0;
112375                     } else if (i === coordsLength - 1) { // i = N-1
112376                         lowerIndex = coordsLength - 1;
112377                         middleIndex = 0;
112378                         upperIndex = 1;
112379                     } else { // i = 0 to N-3
112380                         lowerIndex = i;
112381                         middleIndex = i + 1;
112382                         upperIndex = i + 2;
112383                     }
112384                     p1 = coords[lowerIndex];
112385                     p2 = coords[middleIndex];
112386                     p3 = coords[upperIndex];
112387                     area += (rad$1(p3[0]) - rad$1(p1[0])) * Math.sin(rad$1(p2[1]));
112388                 }
112389
112390                 area = area * RADIUS$1 * RADIUS$1 / 2;
112391             }
112392
112393             return area;
112394         }
112395
112396         function rad$1(_) {
112397             return _ * Math.PI / 180;
112398         }
112399
112400         /**
112401          * Get Geometry from Feature or Geometry Object
112402          *
112403          * @param {Feature|Geometry} geojson GeoJSON Feature or Geometry Object
112404          * @returns {Geometry|null} GeoJSON Geometry Object
112405          * @throws {Error} if geojson is not a Feature or Geometry Object
112406          * @example
112407          * var point = {
112408          *   "type": "Feature",
112409          *   "properties": {},
112410          *   "geometry": {
112411          *     "type": "Point",
112412          *     "coordinates": [110, 40]
112413          *   }
112414          * }
112415          * var geom = turf.getGeom(point)
112416          * //={"type": "Point", "coordinates": [110, 40]}
112417          */
112418         function getGeom(geojson) {
112419             if (!geojson) { throw new Error('geojson is required'); }
112420             if (geojson.geometry !== undefined) { return geojson.geometry; }
112421             if (geojson.coordinates || geojson.geometries) { return geojson; }
112422             throw new Error('geojson must be a valid Feature or Geometry Object');
112423         }
112424
112425         /**
112426          * Finds the difference between two {@link Polygon|polygons} by clipping the second polygon from the first.
112427          *
112428          * @name difference
112429          * @param {Feature<Polygon|MultiPolygon>} polygon1 input Polygon feature
112430          * @param {Feature<Polygon|MultiPolygon>} polygon2 Polygon feature to difference from polygon1
112431          * @returns {Feature<Polygon|MultiPolygon>|null} a Polygon or MultiPolygon feature showing the area of `polygon1` excluding the area of `polygon2` (if empty returns `null`)
112432          * @example
112433          * var polygon1 = turf.polygon([[
112434          *   [128, -26],
112435          *   [141, -26],
112436          *   [141, -21],
112437          *   [128, -21],
112438          *   [128, -26]
112439          * ]], {
112440          *   "fill": "#F00",
112441          *   "fill-opacity": 0.1
112442          * });
112443          * var polygon2 = turf.polygon([[
112444          *   [126, -28],
112445          *   [140, -28],
112446          *   [140, -20],
112447          *   [126, -20],
112448          *   [126, -28]
112449          * ]], {
112450          *   "fill": "#00F",
112451          *   "fill-opacity": 0.1
112452          * });
112453          *
112454          * var difference = turf.difference(polygon1, polygon2);
112455          *
112456          * //addToMap
112457          * var addToMap = [polygon1, polygon2, difference];
112458          */
112459         function difference(polygon1, polygon2) {
112460             var geom1 = getGeom(polygon1);
112461             var geom2 = getGeom(polygon2);
112462             var properties = polygon1.properties || {};
112463
112464             // Issue #721 - JSTS can't handle empty polygons
112465             geom1 = removeEmptyPolygon(geom1);
112466             geom2 = removeEmptyPolygon(geom2);
112467             if (!geom1) { return null; }
112468             if (!geom2) { return feature$1(geom1, properties); }
112469
112470             // JSTS difference operation
112471             var reader = new GeoJSONReader();
112472             var a = reader.read(geom1);
112473             var b = reader.read(geom2);
112474             var differenced = OverlayOp.difference(a, b);
112475             if (differenced.isEmpty()) { return null; }
112476             var writer = new GeoJSONWriter();
112477             var geom = writer.write(differenced);
112478
112479             return feature$1(geom, properties);
112480         }
112481
112482         /**
112483          * Detect Empty Polygon
112484          *
112485          * @private
112486          * @param {Geometry<Polygon|MultiPolygon>} geom Geometry Object
112487          * @returns {Geometry<Polygon|MultiPolygon>|null} removed any polygons with no areas
112488          */
112489         function removeEmptyPolygon(geom) {
112490             switch (geom.type) {
112491             case 'Polygon':
112492                 if (area(geom) > 1) { return geom; }
112493                 return null;
112494             case 'MultiPolygon':
112495                 var coordinates = [];
112496                 flattenEach(geom, function (feature$$1) {
112497                     if (area(feature$$1) > 1) { coordinates.push(feature$$1.geometry.coordinates); }
112498                 });
112499                 if (coordinates.length) { return {type: 'MultiPolygon', coordinates: coordinates}; }
112500             }
112501         }
112502
112503         /**
112504          * 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.
112505          *
112506          * @name union
112507          * @param {...Feature<Polygon>} A polygon to combine
112508          * @returns {Feature<(Polygon|MultiPolygon)>} a combined {@link Polygon} or {@link MultiPolygon} feature
112509          * @example
112510          * var poly1 = turf.polygon([[
112511          *     [-82.574787, 35.594087],
112512          *     [-82.574787, 35.615581],
112513          *     [-82.545261, 35.615581],
112514          *     [-82.545261, 35.594087],
112515          *     [-82.574787, 35.594087]
112516          * ]], {"fill": "#0f0"});
112517          * var poly2 = turf.polygon([[
112518          *     [-82.560024, 35.585153],
112519          *     [-82.560024, 35.602602],
112520          *     [-82.52964, 35.602602],
112521          *     [-82.52964, 35.585153],
112522          *     [-82.560024, 35.585153]
112523          * ]], {"fill": "#00f"});
112524          *
112525          * var union = turf.union(poly1, poly2);
112526          *
112527          * //addToMap
112528          * var addToMap = [poly1, poly2, union];
112529          */
112530         function union$1() {
112531             var arguments$1 = arguments;
112532
112533             var reader = new GeoJSONReader();
112534             var result = reader.read(JSON.stringify(arguments[0].geometry));
112535
112536             for (var i = 1; i < arguments.length; i++) {
112537                 result = UnionOp.union(result, reader.read(JSON.stringify(arguments$1[i].geometry)));
112538             }
112539
112540             var writer = new GeoJSONWriter();
112541             result = writer.write(result);
112542
112543             return {
112544                 type: 'Feature',
112545                 geometry: result,
112546                 properties: arguments[0].properties
112547             };
112548         }
112549
112550         // Reduce an array of locations into a single GeoJSON feature
112551         function _locationReducer(accumulator, location) {
112552           /* eslint-disable no-console, no-invalid-this */
112553           var result;
112554           try {
112555             var resolved = this.resolveLocation(location);
112556             if (!resolved || !resolved.feature) {
112557               console.warn(("Warning:  Couldn't resolve location \"" + location + "\""));
112558               return accumulator;
112559             }
112560             result = !accumulator ? resolved.feature : union$1(accumulator, resolved.feature);
112561           } catch (e) {
112562             console.warn(("Warning:  Error resolving location \"" + location + "\""));
112563             console.warn(e);
112564             result = accumulator;
112565           }
112566
112567           return result;
112568           /* eslint-enable no-console, no-invalid-this */
112569         }
112570
112571
112572
112573         function _cloneDeep(obj) {
112574           return JSON.parse(JSON.stringify(obj));
112575         }
112576
112577
112578         var defaultExport = function defaultExport(fc) {
112579           var this$1 = this;
112580
112581           this._cache = {};
112582
112583           // process input FeatureCollection
112584           if (fc && fc.type === 'FeatureCollection' && Array.isArray(fc.features)) {
112585             fc.features.forEach(function (feature) {
112586               feature.properties = feature.properties || {};
112587               var props = feature.properties;
112588
112589               // get `id` from either `id` or `properties`
112590               var id = feature.id || props.id;
112591               if (!id || !/^\S+\.geojson$/i.test(id)) { return; }
112592
112593               // ensure id exists and is lowercase
112594               id = id.toLowerCase();
112595               feature.id = id;
112596               props.id = id;
112597
112598               // ensure area property exists
112599               if (!props.area) {
112600                 var area = geojsonArea.geometry(feature.geometry) / 1e6;// m² to km²
112601                 props.area = Number(area.toFixed(2));
112602               }
112603
112604               this$1._cache[id] = feature;
112605             });
112606           }
112607
112608           // Replace CountryCoder world geometry to have a polygon covering the world.
112609           var world = _cloneDeep(feature('Q2'));
112610           world.geometry = {
112611             type: 'Polygon',
112612             coordinates: [[[-180, -90], [180, -90], [180, 90], [-180, 90], [-180, -90]]]
112613           };
112614           world.id = 'Q2';
112615           world.properties.id = 'Q2';
112616           world.properties.area = geojsonArea.geometry(world.geometry) / 1e6;// m² to km²
112617           this._cache.Q2 = world;
112618         };
112619
112620
112621         // validateLocation
112622         //
112623         // Pass a `location` identifier
112624         // Returns a result like
112625         // {
112626         //   type:   'point', 'geojson', or 'countrycoder'
112627         //   location:the queried location
112628         //   id:      a unique identifier
112629         // }
112630         //or `null` if the location is invalid
112631         //
112632         defaultExport.prototype.validateLocation = function validateLocation (location) {
112633           if (Array.isArray(location)) { // a [lon,lat] coordinate pair?
112634             if (location.length === 2 && Number.isFinite(location[0]) && Number.isFinite(location[1]) &&
112635               location[0] >= -180 && location[0] <= 180 && location[1] >= -90 && location[1] <= 90
112636             ) {
112637               var id = '[' + location.toString() + ']';
112638               return { type: 'point', location: location, id: id };
112639             }
112640
112641           } else if (typeof location === 'string' && /^\S+\.geojson$/i.test(location)) { // a .geojson filename?
112642             var id$1 = location.toLowerCase();
112643             if (this._cache[id$1]) {
112644               return { type: 'geojson', location: location, id: id$1 };
112645             }
112646
112647           } else if (typeof location === 'string' || typeof location === 'number') { // a country-coder value?
112648             var feature$1 = feature(location);
112649             if (feature$1) {
112650               // Use wikidata QID as the identifier, since that seems to be the only
112651               // property that everything in CountryCoder is guaranteed to have.
112652               var id$2 = feature$1.properties.wikidata;
112653               return { type: 'countrycoder', location: location, id: id$2 };
112654             }
112655           }
112656
112657           return null;
112658         };
112659
112660
112661         // resolveLocation
112662         //
112663         // Pass a `location` identifier
112664         // Returns a result like
112665         // {
112666         //   type:    'point', 'geojson', or 'countrycoder'
112667         //   location:the queried location
112668         //   id:      a unique identifier
112669         //   feature: the geojson feature
112670         // }
112671         //or `null` if the location is invalid
112672         //
112673         defaultExport.prototype.resolveLocation = function resolveLocation (location) {
112674           var valid = this.validateLocation(location);
112675           if (!valid) { return null; }
112676
112677           // return a result from cache if we can
112678           if (this._cache[valid.id]) {
112679             return Object.assign(valid, { feature: this._cache[valid.id] });
112680           }
112681
112682           // a [lon,lat] coordinate pair?
112683           if (valid.type === 'point') {
112684             var RADIUS = 25000;// meters
112685             var EDGES = 10;
112686             var PRECISION = 3;
112687             var area = Math.PI * RADIUS * RADIUS / 1e6;   // m² to km²
112688             var feature$1 = this._cache[valid.id] = geojsonPrecision({
112689               type: 'Feature',
112690               id: valid.id,
112691               properties: { id: valid.id, area: Number(area.toFixed(2)) },
112692               geometry: circleToPolygon(location, RADIUS, EDGES)
112693             }, PRECISION);
112694             return Object.assign(valid, { feature: feature$1 });
112695
112696           // a .geojson filename?
112697           } else if (valid.type === 'geojson') ; else if (valid.type === 'countrycoder') {
112698             var feature$1$1 = _cloneDeep(feature(valid.id));
112699             var props = feature$1$1.properties;
112700
112701             // -> This block of code is weird and requires some explanation. <-
112702             // CountryCoder includes higher level features which are made up of members.
112703             // These features don't have their own geometry, but CountryCoder provides an
112704             // `aggregateFeature` method to combine these members into a MultiPolygon.
112705             // BUT, when we try to actually work with these aggregated MultiPolygons,
112706             // Turf/JSTS gets crashy because of topography bugs.
112707             // SO, we'll aggregate the features ourselves by unioning them together.
112708             // This approach also has the benefit of removing all the internal boaders and
112709             // simplifying the regional polygons a lot.
112710             if (Array.isArray(props.members)) {
112711               var seed = feature$1$1.geometry ? feature$1$1 : null;
112712               var aggregate = props.members.reduce(_locationReducer.bind(this), seed);
112713               feature$1$1.geometry = aggregate.geometry;
112714             }
112715
112716             // ensure area property exists
112717             if (!props.area) {
112718               var area$1 = geojsonArea.geometry(feature$1$1.geometry) / 1e6;// m² to km²
112719               props.area = Number(area$1.toFixed(2));
112720             }
112721
112722             // ensure id property exists
112723             feature$1$1.id = valid.id;
112724             props.id = valid.id;
112725
112726             this._cache[valid.id] = feature$1$1;
112727             return Object.assign(valid, { feature: feature$1$1 });
112728           }
112729
112730           return null;
112731         };
112732
112733
112734         // resolveLocationSet
112735         //
112736         // Pass a `locationSet` Object like:
112737         // `{ include: [ Array ], exclude: [ Array ] }`
112738         // Returns a stable identifier string of the form:
112739         // "+[included]-[excluded]"
112740         //
112741         defaultExport.prototype.resolveLocationSet = function resolveLocationSet (locationSet) {
112742           locationSet = locationSet || {};
112743           var resolve = this.resolveLocation.bind(this);
112744           var include = (locationSet.include || []).map(resolve).filter(Boolean);
112745           var exclude = (locationSet.exclude || []).map(resolve).filter(Boolean);
112746
112747           if (!include.length) {
112748             include = [resolve('Q2')]; // default to 'the world'
112749           }
112750
112751           // return quickly if it's a single included location..
112752           if (include.length === 1 && exclude.length === 0) {
112753             return include[0].feature;
112754           }
112755
112756           // generate stable identifier
112757           include.sort(sortFeatures);
112758           var id = '+[' + include.map(function (d) { return d.id; }).join(',') + ']';
112759           if (exclude.length) {
112760             exclude.sort(sortFeatures);
112761             id += '-[' + exclude.map(function (d) { return d.id; }).join(',') + ']';
112762           }
112763
112764           // return cached?
112765           if (this._cache[id]) {
112766             return this._cache[id];
112767           }
112768
112769           // calculate unions
112770           var includeGeoJSON = include.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112771           var excludeGeoJSON = exclude.map(function (d) { return d.location; }).reduce(_locationReducer.bind(this), null);
112772
112773           // calculate difference, update area and return result
112774           var resultGeoJSON = excludeGeoJSON ? difference(includeGeoJSON, excludeGeoJSON) : includeGeoJSON;
112775           var area = geojsonArea.geometry(resultGeoJSON.geometry) / 1e6;// m² to km²
112776           resultGeoJSON.id = id;
112777           resultGeoJSON.properties = { id: id, area: Number(area.toFixed(2)) };
112778
112779           return this._cache[id] = resultGeoJSON;
112780
112781
112782           // Sorting the location lists is ok because they end up unioned together.
112783           // This sorting makes it possible to generate a deterministic id.
112784           function sortFeatures(a, b) {
112785             var rank = { countrycoder: 1, geojson: 2, point: 3 };
112786             var aRank = rank[a.type];
112787             var bRank = rank[b.type];
112788
112789             return (aRank > bRank) ? 1
112790               : (aRank < bRank) ? -1
112791               : a.id.localeCompare(b.id);
112792           }
112793         };
112794
112795
112796         defaultExport.prototype.cache = function cache () {
112797           return this._cache;
112798         };
112799
112800         var _oci = null;
112801
112802         function uiSuccess(context) {
112803           var MAXEVENTS = 2;
112804           var dispatch$1 = dispatch('cancel');
112805           var _changeset;
112806           var _location;
112807           ensureOSMCommunityIndex();   // start fetching the data
112808
112809
112810           function ensureOSMCommunityIndex() {
112811             var data = _mainFileFetcher;
112812             return Promise.all([ data.get('oci_resources'), data.get('oci_features') ])
112813               .then(function (vals) {
112814                 if (_oci) { return _oci; }
112815
112816                 var ociResources = vals[0].resources;
112817                 var loco = new defaultExport(vals[1]);
112818                 var ociFeatures = {};
112819
112820                 Object.values(ociResources).forEach(function (resource) {
112821                   var feature = loco.resolveLocationSet(resource.locationSet);
112822                   var ociFeature = ociFeatures[feature.id];
112823                   if (!ociFeature) {
112824                     ociFeature = JSON.parse(JSON.stringify(feature));  // deep clone
112825                     ociFeature.properties.resourceIDs = new Set();
112826                     ociFeatures[feature.id] = ociFeature;
112827                   }
112828                   ociFeature.properties.resourceIDs.add(resource.id);
112829                 });
112830
112831                 return _oci = {
112832                   features: ociFeatures,
112833                   resources: ociResources,
112834                   query: whichPolygon_1({ type: 'FeatureCollection', features: Object.values(ociFeatures) })
112835                 };
112836               });
112837           }
112838
112839
112840           // string-to-date parsing in JavaScript is weird
112841           function parseEventDate(when) {
112842             if (!when) { return; }
112843
112844             var raw = when.trim();
112845             if (!raw) { return; }
112846
112847             if (!/Z$/.test(raw)) {   // if no trailing 'Z', add one
112848               raw += 'Z';            // this forces date to be parsed as a UTC date
112849             }
112850
112851             var parsed = new Date(raw);
112852             return new Date(parsed.toUTCString().substr(0, 25));  // convert to local timezone
112853           }
112854
112855
112856           function success(selection) {
112857             var header = selection
112858               .append('div')
112859               .attr('class', 'header fillL');
112860
112861             header
112862               .append('h3')
112863               .text(_t('success.just_edited'));
112864
112865             header
112866               .append('button')
112867               .attr('class', 'close')
112868               .on('click', function () { return dispatch$1.call('cancel'); })
112869               .call(svgIcon('#iD-icon-close'));
112870
112871             var body = selection
112872               .append('div')
112873               .attr('class', 'body save-success fillL');
112874
112875             var summary = body
112876               .append('div')
112877               .attr('class', 'save-summary');
112878
112879             summary
112880               .append('h3')
112881               .text(_t('success.thank_you' + (_location ? '_location' : ''), { where: _location }));
112882
112883             summary
112884               .append('p')
112885               .text(_t('success.help_html'))
112886               .append('a')
112887               .attr('class', 'link-out')
112888               .attr('target', '_blank')
112889               .attr('tabindex', -1)
112890               .attr('href', _t('success.help_link_url'))
112891               .call(svgIcon('#iD-icon-out-link', 'inline'))
112892               .append('span')
112893               .text(_t('success.help_link_text'));
112894
112895             var osm = context.connection();
112896             if (!osm) { return; }
112897
112898             var changesetURL = osm.changesetURL(_changeset.id);
112899
112900             var table = summary
112901               .append('table')
112902               .attr('class', 'summary-table');
112903
112904             var row = table
112905               .append('tr')
112906               .attr('class', 'summary-row');
112907
112908             row
112909               .append('td')
112910               .attr('class', 'cell-icon summary-icon')
112911               .append('a')
112912               .attr('target', '_blank')
112913               .attr('href', changesetURL)
112914               .append('svg')
112915               .attr('class', 'logo-small')
112916               .append('use')
112917               .attr('xlink:href', '#iD-logo-osm');
112918
112919             var summaryDetail = row
112920               .append('td')
112921               .attr('class', 'cell-detail summary-detail');
112922
112923             summaryDetail
112924               .append('a')
112925               .attr('class', 'cell-detail summary-view-on-osm')
112926               .attr('target', '_blank')
112927               .attr('href', changesetURL)
112928               .text(_t('success.view_on_osm'));
112929
112930             summaryDetail
112931               .append('div')
112932               .html(_t('success.changeset_id', {
112933                 changeset_id: ("<a href=\"" + changesetURL + "\" target=\"_blank\">" + (_changeset.id) + "</a>")
112934               }));
112935
112936
112937             // Get OSM community index features intersecting the map..
112938             ensureOSMCommunityIndex()
112939               .then(function (oci) {
112940                 var communities = [];
112941                 var properties = oci.query(context.map().center(), true) || [];
112942
112943                 // Gather the communities from the result
112944                 properties.forEach(function (props) {
112945                   var resourceIDs = Array.from(props.resourceIDs);
112946                   resourceIDs.forEach(function (resourceID) {
112947                     var resource = oci.resources[resourceID];
112948                     communities.push({
112949                       area: props.area || Infinity,
112950                       order: resource.order || 0,
112951                       resource: resource
112952                     });
112953                   });
112954                 });
112955
112956                 // sort communities by feature area ascending, community order descending
112957                 communities.sort(function (a, b) { return a.area - b.area || b.order - a.order; });
112958
112959                 body
112960                   .call(showCommunityLinks, communities.map(function (c) { return c.resource; }));
112961               });
112962           }
112963
112964
112965           function showCommunityLinks(selection, resources) {
112966             var communityLinks = selection
112967               .append('div')
112968               .attr('class', 'save-communityLinks');
112969
112970             communityLinks
112971               .append('h3')
112972               .text(_t('success.like_osm'));
112973
112974             var table = communityLinks
112975               .append('table')
112976               .attr('class', 'community-table');
112977
112978             var row = table.selectAll('.community-row')
112979               .data(resources);
112980
112981             var rowEnter = row.enter()
112982               .append('tr')
112983               .attr('class', 'community-row');
112984
112985             rowEnter
112986               .append('td')
112987               .attr('class', 'cell-icon community-icon')
112988               .append('a')
112989               .attr('target', '_blank')
112990               .attr('href', function (d) { return d.url; })
112991               .append('svg')
112992               .attr('class', 'logo-small')
112993               .append('use')
112994               .attr('xlink:href', function (d) { return ("#community-" + (d.type)); });
112995
112996             var communityDetail = rowEnter
112997               .append('td')
112998               .attr('class', 'cell-detail community-detail');
112999
113000             communityDetail
113001               .each(showCommunityDetails);
113002
113003             communityLinks
113004               .append('div')
113005               .attr('class', 'community-missing')
113006               .text(_t('success.missing'))
113007               .append('a')
113008               .attr('class', 'link-out')
113009               .attr('target', '_blank')
113010               .attr('tabindex', -1)
113011               .call(svgIcon('#iD-icon-out-link', 'inline'))
113012               .attr('href', 'https://github.com/osmlab/osm-community-index/issues')
113013               .append('span')
113014               .text(_t('success.tell_us'));
113015           }
113016
113017
113018           function showCommunityDetails(d) {
113019             var selection = select(this);
113020             var communityID = d.id;
113021             var replacements = {
113022               url: linkify(d.url),
113023               signupUrl: linkify(d.signupUrl || d.url)
113024             };
113025
113026             selection
113027               .append('div')
113028               .attr('class', 'community-name')
113029               .append('a')
113030               .attr('target', '_blank')
113031               .attr('href', d.url)
113032               .text(_t(("community." + (d.id) + ".name")));
113033
113034             var descriptionHTML = _t(("community." + (d.id) + ".description"), replacements);
113035
113036             if (d.type === 'reddit') {   // linkify subreddits  #4997
113037               descriptionHTML = descriptionHTML
113038                 .replace(/(\/r\/\w*\/*)/i, function (match) { return linkify(d.url, match); });
113039             }
113040
113041             selection
113042               .append('div')
113043               .attr('class', 'community-description')
113044               .html(descriptionHTML);
113045
113046             if (d.extendedDescription || (d.languageCodes && d.languageCodes.length)) {
113047               selection
113048                 .append('div')
113049                 .call(uiDisclosure(context, ("community-more-" + (d.id)), false)
113050                   .expanded(false)
113051                   .updatePreference(false)
113052                   .title(_t('success.more'))
113053                   .content(showMore)
113054                 );
113055             }
113056
113057             var nextEvents = (d.events || [])
113058               .map(function (event) {
113059                 event.date = parseEventDate(event.when);
113060                 return event;
113061               })
113062               .filter(function (event) {      // date is valid and future (or today)
113063                 var t = event.date.getTime();
113064                 var now = (new Date()).setHours(0,0,0,0);
113065                 return !isNaN(t) && t >= now;
113066               })
113067               .sort(function (a, b) {       // sort by date ascending
113068                 return a.date < b.date ? -1 : a.date > b.date ? 1 : 0;
113069               })
113070               .slice(0, MAXEVENTS);   // limit number of events shown
113071
113072             if (nextEvents.length) {
113073               selection
113074                 .append('div')
113075                 .call(uiDisclosure(context, ("community-events-" + (d.id)), false)
113076                   .expanded(false)
113077                   .updatePreference(false)
113078                   .title(_t('success.events'))
113079                   .content(showNextEvents)
113080                 )
113081                 .select('.hide-toggle')
113082                 .append('span')
113083                 .attr('class', 'badge-text')
113084                 .text(nextEvents.length);
113085             }
113086
113087
113088             function showMore(selection) {
113089               var more = selection.selectAll('.community-more')
113090                 .data([0]);
113091
113092               var moreEnter = more.enter()
113093                 .append('div')
113094                 .attr('class', 'community-more');
113095
113096               if (d.extendedDescription) {
113097                 moreEnter
113098                   .append('div')
113099                   .attr('class', 'community-extended-description')
113100                   .html(_t(("community." + (d.id) + ".extendedDescription"), replacements));
113101               }
113102
113103               if (d.languageCodes && d.languageCodes.length) {
113104                 var languageList = d.languageCodes
113105                   .map(function (code) { return _mainLocalizer.languageName(code); })
113106                   .join(', ');
113107
113108                 moreEnter
113109                   .append('div')
113110                   .attr('class', 'community-languages')
113111                   .text(_t('success.languages', { languages: languageList }));
113112               }
113113             }
113114
113115
113116             function showNextEvents(selection) {
113117               var events = selection
113118                 .append('div')
113119                 .attr('class', 'community-events');
113120
113121               var item = events.selectAll('.community-event')
113122                 .data(nextEvents);
113123
113124               var itemEnter = item.enter()
113125                 .append('div')
113126                 .attr('class', 'community-event');
113127
113128               itemEnter
113129                 .append('div')
113130                 .attr('class', 'community-event-name')
113131                 .append('a')
113132                 .attr('target', '_blank')
113133                 .attr('href', function (d) { return d.url; })
113134                 .text(function (d) {
113135                   var name = d.name;
113136                   if (d.i18n && d.id) {
113137                     name = _t(("community." + communityID + ".events." + (d.id) + ".name"), { default: name });
113138                   }
113139                   return name;
113140                 });
113141
113142               itemEnter
113143                 .append('div')
113144                 .attr('class', 'community-event-when')
113145                 .text(function (d) {
113146                   var options = { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' };
113147                   if (d.date.getHours() || d.date.getMinutes()) {   // include time if it has one
113148                     options.hour = 'numeric';
113149                     options.minute = 'numeric';
113150                   }
113151                   return d.date.toLocaleString(_mainLocalizer.localeCode(), options);
113152                 });
113153
113154               itemEnter
113155                 .append('div')
113156                 .attr('class', 'community-event-where')
113157                 .text(function (d) {
113158                   var where = d.where;
113159                   if (d.i18n && d.id) {
113160                     where = _t(("community." + communityID + ".events." + (d.id) + ".where"), { default: where });
113161                   }
113162                   return where;
113163                 });
113164
113165               itemEnter
113166                 .append('div')
113167                 .attr('class', 'community-event-description')
113168                 .text(function (d) {
113169                   var description = d.description;
113170                   if (d.i18n && d.id) {
113171                     description = _t(("community." + communityID + ".events." + (d.id) + ".description"), { default: description });
113172                   }
113173                   return description;
113174                 });
113175             }
113176
113177
113178             function linkify(url, text) {
113179               text = text || url;
113180               return ("<a target=\"_blank\" href=\"" + url + "\">" + text + "</a>");
113181             }
113182           }
113183
113184
113185           success.changeset = function(val) {
113186             if (!arguments.length) { return _changeset; }
113187             _changeset = val;
113188             return success;
113189           };
113190
113191
113192           success.location = function(val) {
113193             if (!arguments.length) { return _location; }
113194             _location = val;
113195             return success;
113196           };
113197
113198
113199           return utilRebind(success, dispatch$1, 'on');
113200         }
113201
113202         function modeSave(context) {
113203             var mode = { id: 'save' };
113204             var keybinding = utilKeybinding('modeSave');
113205
113206             var commit = uiCommit(context)
113207                 .on('cancel', cancel);
113208             var _conflictsUi; // uiConflicts
113209
113210             var _location;
113211             var _success;
113212
113213             var uploader = context.uploader()
113214                 .on('saveStarted.modeSave', function() {
113215                     keybindingOff();
113216                 })
113217                 // fire off some async work that we want to be ready later
113218                 .on('willAttemptUpload.modeSave', prepareForSuccess)
113219                 .on('progressChanged.modeSave', showProgress)
113220                 .on('resultNoChanges.modeSave', function() {
113221                     cancel();
113222                 })
113223                 .on('resultErrors.modeSave', showErrors)
113224                 .on('resultConflicts.modeSave', showConflicts)
113225                 .on('resultSuccess.modeSave', showSuccess);
113226
113227
113228             function cancel() {
113229                 context.enter(modeBrowse(context));
113230             }
113231
113232
113233             function showProgress(num, total) {
113234                 var modal = context.container().select('.loading-modal .modal-section');
113235                 var progress = modal.selectAll('.progress')
113236                     .data([0]);
113237
113238                 // enter/update
113239                 progress.enter()
113240                     .append('div')
113241                     .attr('class', 'progress')
113242                     .merge(progress)
113243                     .text(_t('save.conflict_progress', { num: num, total: total }));
113244             }
113245
113246
113247             function showConflicts(changeset, conflicts, origChanges) {
113248
113249                 var selection = context.container()
113250                     .select('.sidebar')
113251                     .append('div')
113252                     .attr('class','sidebar-component');
113253
113254                 context.container().selectAll('.main-content')
113255                     .classed('active', true)
113256                     .classed('inactive', false);
113257
113258                 _conflictsUi = uiConflicts(context)
113259                     .conflictList(conflicts)
113260                     .origChanges(origChanges)
113261                     .on('cancel', function() {
113262                         context.container().selectAll('.main-content')
113263                             .classed('active', false)
113264                             .classed('inactive', true);
113265                         selection.remove();
113266                         keybindingOn();
113267
113268                         uploader.cancelConflictResolution();
113269                     })
113270                     .on('save', function() {
113271                         context.container().selectAll('.main-content')
113272                             .classed('active', false)
113273                             .classed('inactive', true);
113274                         selection.remove();
113275
113276                         uploader.processResolvedConflicts(changeset);
113277                     });
113278
113279                 selection.call(_conflictsUi);
113280             }
113281
113282
113283             function showErrors(errors) {
113284                 keybindingOn();
113285
113286                 var selection = uiConfirm(context.container());
113287                 selection
113288                     .select('.modal-section.header')
113289                     .append('h3')
113290                     .text(_t('save.error'));
113291
113292                 addErrors(selection, errors);
113293                 selection.okButton();
113294             }
113295
113296
113297             function addErrors(selection, data) {
113298                 var message = selection
113299                     .select('.modal-section.message-text');
113300
113301                 var items = message
113302                     .selectAll('.error-container')
113303                     .data(data);
113304
113305                 var enter = items.enter()
113306                     .append('div')
113307                     .attr('class', 'error-container');
113308
113309                 enter
113310                     .append('a')
113311                     .attr('class', 'error-description')
113312                     .attr('href', '#')
113313                     .classed('hide-toggle', true)
113314                     .text(function(d) { return d.msg || _t('save.unknown_error_details'); })
113315                     .on('click', function() {
113316                         event.preventDefault();
113317
113318                         var error = select(this);
113319                         var detail = select(this.nextElementSibling);
113320                         var exp = error.classed('expanded');
113321
113322                         detail.style('display', exp ? 'none' : 'block');
113323                         error.classed('expanded', !exp);
113324                     });
113325
113326                 var details = enter
113327                     .append('div')
113328                     .attr('class', 'error-detail-container')
113329                     .style('display', 'none');
113330
113331                 details
113332                     .append('ul')
113333                     .attr('class', 'error-detail-list')
113334                     .selectAll('li')
113335                     .data(function(d) { return d.details || []; })
113336                     .enter()
113337                     .append('li')
113338                     .attr('class', 'error-detail-item')
113339                     .text(function(d) { return d; });
113340
113341                 items.exit()
113342                     .remove();
113343             }
113344
113345
113346             function showSuccess(changeset) {
113347                 commit.reset();
113348
113349                 var ui = _success
113350                     .changeset(changeset)
113351                     .location(_location)
113352                     .on('cancel', function() { context.ui().sidebar.hide(); });
113353
113354                 context.enter(modeBrowse(context).sidebar(ui));
113355             }
113356
113357
113358             function keybindingOn() {
113359                 select(document)
113360                     .call(keybinding.on('⎋', cancel, true));
113361             }
113362
113363
113364             function keybindingOff() {
113365                 select(document)
113366                     .call(keybinding.unbind);
113367             }
113368
113369
113370             // Reverse geocode current map location so we can display a message on
113371             // the success screen like "Thank you for editing around place, region."
113372             function prepareForSuccess() {
113373                 _success = uiSuccess(context);
113374                 _location = null;
113375                 if (!services.geocoder) { return; }
113376
113377                 services.geocoder.reverse(context.map().center(), function(err, result) {
113378                     if (err || !result || !result.address) { return; }
113379
113380                     var addr = result.address;
113381                     var place = (addr && (addr.town || addr.city || addr.county)) || '';
113382                     var region = (addr && (addr.state || addr.country)) || '';
113383                     var separator = (place && region) ? _t('success.thank_you_where.separator') : '';
113384
113385                     _location = _t('success.thank_you_where.format',
113386                         { place: place, separator: separator, region: region }
113387                     );
113388                 });
113389             }
113390
113391
113392             mode.selectedIDs = function() {
113393                 return _conflictsUi ? _conflictsUi.shownEntityIds() : [];
113394             };
113395
113396
113397             mode.enter = function() {
113398                 // Show sidebar
113399                 context.ui().sidebar.expand();
113400
113401                 function done() {
113402                     context.ui().sidebar.show(commit);
113403                 }
113404
113405                 keybindingOn();
113406
113407                 context.container().selectAll('.main-content')
113408                     .classed('active', false)
113409                     .classed('inactive', true);
113410
113411                 var osm = context.connection();
113412                 if (!osm) {
113413                     cancel();
113414                     return;
113415                 }
113416
113417                 if (osm.authenticated()) {
113418                     done();
113419                 } else {
113420                     osm.authenticate(function(err) {
113421                         if (err) {
113422                             cancel();
113423                         } else {
113424                             done();
113425                         }
113426                     });
113427                 }
113428             };
113429
113430
113431             mode.exit = function() {
113432
113433                 keybindingOff();
113434
113435                 context.container().selectAll('.main-content')
113436                     .classed('active', true)
113437                     .classed('inactive', false);
113438
113439                 context.ui().sidebar.hide();
113440             };
113441
113442             return mode;
113443         }
113444
113445         function uiToolOldDrawModes(context) {
113446
113447             var tool = {
113448                 id: 'old_modes',
113449                 label: _t('toolbar.add_feature')
113450             };
113451
113452             var modes = [
113453                 modeAddPoint(context, {
113454                     title: _t('modes.add_point.title'),
113455                     button: 'point',
113456                     description: _t('modes.add_point.description'),
113457                     preset: _mainPresetIndex.item('point'),
113458                     key: '1'
113459                 }),
113460                 modeAddLine(context, {
113461                     title: _t('modes.add_line.title'),
113462                     button: 'line',
113463                     description: _t('modes.add_line.description'),
113464                     preset: _mainPresetIndex.item('line'),
113465                     key: '2'
113466                 }),
113467                 modeAddArea(context, {
113468                     title: _t('modes.add_area.title'),
113469                     button: 'area',
113470                     description: _t('modes.add_area.description'),
113471                     preset: _mainPresetIndex.item('area'),
113472                     key: '3'
113473                 })
113474             ];
113475
113476
113477             function enabled() {
113478                 return osmEditable();
113479             }
113480
113481             function osmEditable() {
113482                 return context.editable();
113483             }
113484
113485             modes.forEach(function(mode) {
113486                 context.keybinding().on(mode.key, function() {
113487                     if (!enabled()) { return; }
113488
113489                     if (mode.id === context.mode().id) {
113490                         context.enter(modeBrowse(context));
113491                     } else {
113492                         context.enter(mode);
113493                     }
113494                 });
113495             });
113496
113497             tool.render = function(selection) {
113498
113499                 var wrap = selection
113500                     .append('div')
113501                     .attr('class', 'joined')
113502                     .style('display', 'flex');
113503
113504                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113505
113506                 context.map()
113507                     .on('move.modes', debouncedUpdate)
113508                     .on('drawn.modes', debouncedUpdate);
113509
113510                 context
113511                     .on('enter.modes', update);
113512
113513                 update();
113514
113515
113516                 function update() {
113517
113518                     var buttons = wrap.selectAll('button.add-button')
113519                         .data(modes, function(d) { return d.id; });
113520
113521                     // exit
113522                     buttons.exit()
113523                         .remove();
113524
113525                     // enter
113526                     var buttonsEnter = buttons.enter()
113527                         .append('button')
113528                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113529                         .on('click.mode-buttons', function(d) {
113530                             if (!enabled()) { return; }
113531
113532                             // When drawing, ignore accidental clicks on mode buttons - #4042
113533                             var currMode = context.mode().id;
113534                             if (/^draw/.test(currMode)) { return; }
113535
113536                             if (d.id === currMode) {
113537                                 context.enter(modeBrowse(context));
113538                             } else {
113539                                 context.enter(d);
113540                             }
113541                         })
113542                         .call(uiTooltip()
113543                             .placement('bottom')
113544                             .title(function(d) { return d.description; })
113545                             .keys(function(d) { return [d.key]; })
113546                             .scrollContainer(context.container().select('.top-toolbar'))
113547                         );
113548
113549                     buttonsEnter
113550                         .each(function(d) {
113551                             select(this)
113552                                 .call(svgIcon('#iD-icon-' + d.button));
113553                         });
113554
113555                     buttonsEnter
113556                         .append('span')
113557                         .attr('class', 'label')
113558                         .text(function(mode) { return mode.title; });
113559
113560                     // if we are adding/removing the buttons, check if toolbar has overflowed
113561                     if (buttons.enter().size() || buttons.exit().size()) {
113562                         context.ui().checkOverflow('.top-toolbar', true);
113563                     }
113564
113565                     // update
113566                     buttons = buttons
113567                         .merge(buttonsEnter)
113568                         .classed('disabled', function(d) { return !enabled(); })
113569                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113570                 }
113571             };
113572
113573             return tool;
113574         }
113575
113576         function uiToolNotes(context) {
113577
113578             var tool = {
113579                 id: 'notes',
113580                 label: _t('modes.add_note.label')
113581             };
113582
113583             var mode = modeAddNote(context);
113584
113585             function enabled() {
113586                 return notesEnabled() && notesEditable();
113587             }
113588
113589             function notesEnabled() {
113590                 var noteLayer = context.layers().layer('notes');
113591                 return noteLayer && noteLayer.enabled();
113592             }
113593
113594             function notesEditable() {
113595                 var mode = context.mode();
113596                 return context.map().notesEditable() && mode && mode.id !== 'save';
113597             }
113598
113599             context.keybinding().on(mode.key, function() {
113600                 if (!enabled()) { return; }
113601
113602                 if (mode.id === context.mode().id) {
113603                     context.enter(modeBrowse(context));
113604                 } else {
113605                     context.enter(mode);
113606                 }
113607             });
113608
113609             tool.render = function(selection) {
113610
113611
113612                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113613
113614                 context.map()
113615                     .on('move.notes', debouncedUpdate)
113616                     .on('drawn.notes', debouncedUpdate);
113617
113618                 context
113619                     .on('enter.notes', update);
113620
113621                 update();
113622
113623
113624                 function update() {
113625                     var showNotes = notesEnabled();
113626                     var data = showNotes ? [mode] : [];
113627
113628                     var buttons = selection.selectAll('button.add-button')
113629                         .data(data, function(d) { return d.id; });
113630
113631                     // exit
113632                     buttons.exit()
113633                         .remove();
113634
113635                     // enter
113636                     var buttonsEnter = buttons.enter()
113637                         .append('button')
113638                         .attr('tabindex', -1)
113639                         .attr('class', function(d) { return d.id + ' add-button bar-button'; })
113640                         .on('click.notes', function(d) {
113641                             if (!enabled()) { return; }
113642
113643                             // When drawing, ignore accidental clicks on mode buttons - #4042
113644                             var currMode = context.mode().id;
113645                             if (/^draw/.test(currMode)) { return; }
113646
113647                             if (d.id === currMode) {
113648                                 context.enter(modeBrowse(context));
113649                             } else {
113650                                 context.enter(d);
113651                             }
113652                         })
113653                         .call(uiTooltip()
113654                             .placement('bottom')
113655                             .title(function(d) { return d.description; })
113656                             .keys(function(d) { return [d.key]; })
113657                             .scrollContainer(context.container().select('.top-toolbar'))
113658                         );
113659
113660                     buttonsEnter
113661                         .each(function(d) {
113662                             select(this)
113663                                 .call(svgIcon(d.icon || '#iD-icon-' + d.button));
113664                         });
113665
113666                     // if we are adding/removing the buttons, check if toolbar has overflowed
113667                     if (buttons.enter().size() || buttons.exit().size()) {
113668                         context.ui().checkOverflow('.top-toolbar', true);
113669                     }
113670
113671                     // update
113672                     buttons = buttons
113673                         .merge(buttonsEnter)
113674                         .classed('disabled', function(d) { return !enabled(); })
113675                         .classed('active', function(d) { return context.mode() && context.mode().button === d.button; });
113676                 }
113677             };
113678
113679             tool.uninstall = function() {
113680                 context
113681                     .on('enter.editor.notes', null)
113682                     .on('exit.editor.notes', null)
113683                     .on('enter.notes', null);
113684
113685                 context.map()
113686                     .on('move.notes', null)
113687                     .on('drawn.notes', null);
113688             };
113689
113690             return tool;
113691         }
113692
113693         function uiToolSave(context) {
113694
113695             var tool = {
113696                 id: 'save',
113697                 label: _t('save.title')
113698             };
113699
113700             var button = null;
113701             var tooltipBehavior = null;
113702             var history = context.history();
113703             var key = uiCmd('⌘S');
113704             var _numChanges = 0;
113705
113706             function isSaving() {
113707                 var mode = context.mode();
113708                 return mode && mode.id === 'save';
113709             }
113710
113711             function isDisabled() {
113712                 return _numChanges === 0 || isSaving();
113713             }
113714
113715             function save() {
113716                 event.preventDefault();
113717                 if (!context.inIntro() && !isSaving() && history.hasChanges()) {
113718                     context.enter(modeSave(context));
113719                 }
113720             }
113721
113722             function bgColor() {
113723                 var step;
113724                 if (_numChanges === 0) {
113725                     return null;
113726                 } else if (_numChanges <= 50) {
113727                     step = _numChanges / 50;
113728                     return d3_interpolateRgb('#fff', '#ff8')(step);  // white -> yellow
113729                 } else {
113730                     step = Math.min((_numChanges - 50) / 50, 1.0);
113731                     return d3_interpolateRgb('#ff8', '#f88')(step);  // yellow -> red
113732                 }
113733             }
113734
113735             function updateCount() {
113736                 var val = history.difference().summary().length;
113737                 if (val === _numChanges) { return; }
113738
113739                 _numChanges = val;
113740
113741                 if (tooltipBehavior) {
113742                     tooltipBehavior
113743                         .title(_t(_numChanges > 0 ? 'save.help' : 'save.no_changes'))
113744                         .keys([key]);
113745                 }
113746
113747                 if (button) {
113748                     button
113749                         .classed('disabled', isDisabled())
113750                         .style('background', bgColor());
113751
113752                     button.select('span.count')
113753                         .text(_numChanges);
113754                 }
113755             }
113756
113757
113758             tool.render = function(selection) {
113759                 tooltipBehavior = uiTooltip()
113760                     .placement('bottom')
113761                     .title(_t('save.no_changes'))
113762                     .keys([key])
113763                     .scrollContainer(context.container().select('.top-toolbar'));
113764
113765                 var lastPointerUpType;
113766
113767                 button = selection
113768                     .append('button')
113769                     .attr('class', 'save disabled bar-button')
113770                     .on('pointerup', function() {
113771                         lastPointerUpType = event.pointerType;
113772                     })
113773                     .on('click', function() {
113774                         event.preventDefault();
113775
113776                         save();
113777
113778                         if (_numChanges === 0 && (
113779                             lastPointerUpType === 'touch' ||
113780                             lastPointerUpType === 'pen')
113781                         ) {
113782                             // there are no tooltips for touch interactions so flash feedback instead
113783                             context.ui().flash
113784                                 .duration(2000)
113785                                 .iconName('#iD-icon-save')
113786                                 .iconClass('disabled')
113787                                 .text(_t('save.no_changes'))();
113788                         }
113789                         lastPointerUpType = null;
113790                     })
113791                     .call(tooltipBehavior);
113792
113793                 button
113794                     .call(svgIcon('#iD-icon-save'));
113795
113796                 button
113797                     .append('span')
113798                     .attr('class', 'count')
113799                     .attr('aria-hidden', 'true')
113800                     .text('0');
113801
113802                 updateCount();
113803
113804
113805                 context.keybinding()
113806                     .on(key, save, true);
113807
113808
113809                 context.history()
113810                     .on('change.save', updateCount);
113811
113812                 context
113813                     .on('enter.save', function() {
113814                         if (button) {
113815                             button
113816                                 .classed('disabled', isDisabled());
113817
113818                             if (isSaving()) {
113819                                 button.call(tooltipBehavior.hide);
113820                             }
113821                         }
113822                     });
113823             };
113824
113825
113826             tool.uninstall = function() {
113827                 context.keybinding()
113828                     .off(key, true);
113829
113830                 context.history()
113831                     .on('change.save', null);
113832
113833                 context
113834                     .on('enter.save', null);
113835
113836                 button = null;
113837                 tooltipBehavior = null;
113838             };
113839
113840             return tool;
113841         }
113842
113843         function uiToolSidebarToggle(context) {
113844
113845             var tool = {
113846                 id: 'sidebar_toggle',
113847                 label: _t('toolbar.inspect')
113848             };
113849
113850             tool.render = function(selection) {
113851                 selection
113852                     .append('button')
113853                     .attr('class', 'bar-button')
113854                     .on('click', function() {
113855                         context.ui().sidebar.toggle();
113856                     })
113857                     .call(uiTooltip()
113858                         .placement('bottom')
113859                         .title(_t('sidebar.tooltip'))
113860                         .keys([_t('sidebar.key')])
113861                         .scrollContainer(context.container().select('.top-toolbar'))
113862                     )
113863                     .call(svgIcon('#iD-icon-sidebar-' + (_mainLocalizer.textDirection() === 'rtl' ? 'right' : 'left')));
113864             };
113865
113866             return tool;
113867         }
113868
113869         function uiToolUndoRedo(context) {
113870
113871             var tool = {
113872                 id: 'undo_redo',
113873                 label: _t('toolbar.undo_redo')
113874             };
113875
113876             var commands = [{
113877                 id: 'undo',
113878                 cmd: uiCmd('⌘Z'),
113879                 action: function() {
113880                     context.undo();
113881                 },
113882                 annotation: function() {
113883                     return context.history().undoAnnotation();
113884                 },
113885                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')
113886             }, {
113887                 id: 'redo',
113888                 cmd: uiCmd('⌘⇧Z'),
113889                 action: function() {
113890                     context.redo();
113891                 },
113892                 annotation: function() {
113893                     return context.history().redoAnnotation();
113894                 },
113895                 icon: 'iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'undo' : 'redo')
113896             }];
113897
113898
113899             function editable() {
113900                 return context.mode() && context.mode().id !== 'save' && context.map().editableDataEnabled(true /* ignore min zoom */);
113901             }
113902
113903
113904             tool.render = function(selection) {
113905                 var tooltipBehavior = uiTooltip()
113906                     .placement('bottom')
113907                     .title(function (d) {
113908                         return d.annotation() ?
113909                             _t(d.id + '.tooltip', { action: d.annotation() }) :
113910                             _t(d.id + '.nothing');
113911                     })
113912                     .keys(function(d) {
113913                         return [d.cmd];
113914                     })
113915                     .scrollContainer(context.container().select('.top-toolbar'));
113916
113917                 var lastPointerUpType;
113918
113919                 var buttons = selection.selectAll('button')
113920                     .data(commands)
113921                     .enter()
113922                     .append('button')
113923                     .attr('class', function(d) { return 'disabled ' + d.id + '-button bar-button'; })
113924                     .on('pointerup', function() {
113925                         // `pointerup` is always called before `click`
113926                         lastPointerUpType = event.pointerType;
113927                     })
113928                     .on('click', function(d) {
113929                         event.preventDefault();
113930
113931                         var annotation = d.annotation();
113932
113933                         if (editable() && annotation) {
113934                             d.action();
113935                         }
113936
113937                         if (editable() && (
113938                             lastPointerUpType === 'touch' ||
113939                             lastPointerUpType === 'pen')
113940                         ) {
113941                             // there are no tooltips for touch interactions so flash feedback instead
113942
113943                             var text = annotation ?
113944                                 _t(d.id + '.tooltip', { action: annotation }) :
113945                                 _t(d.id + '.nothing');
113946                             context.ui().flash
113947                                 .duration(2000)
113948                                 .iconName('#' + d.icon)
113949                                 .iconClass(annotation ? '' : 'disabled')
113950                                 .text(text)();
113951                         }
113952                         lastPointerUpType = null;
113953                     })
113954                     .call(tooltipBehavior);
113955
113956                 buttons.each(function(d) {
113957                     select(this)
113958                         .call(svgIcon('#' + d.icon));
113959                 });
113960
113961                 context.keybinding()
113962                     .on(commands[0].cmd, function() {
113963                         event.preventDefault();
113964                         if (editable()) { commands[0].action(); }
113965                     })
113966                     .on(commands[1].cmd, function() {
113967                         event.preventDefault();
113968                         if (editable()) { commands[1].action(); }
113969                     });
113970
113971
113972                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
113973
113974                 context.map()
113975                     .on('move.undo_redo', debouncedUpdate)
113976                     .on('drawn.undo_redo', debouncedUpdate);
113977
113978                 context.history()
113979                     .on('change.undo_redo', function(difference) {
113980                         if (difference) { update(); }
113981                     });
113982
113983                 context
113984                     .on('enter.undo_redo', update);
113985
113986
113987                 function update() {
113988                     buttons
113989                         .classed('disabled', function(d) {
113990                             return !editable() || !d.annotation();
113991                         })
113992                         .each(function() {
113993                             var selection = select(this);
113994                             if (!selection.select('.tooltip.in').empty()) {
113995                                 selection.call(tooltipBehavior.updateContent);
113996                             }
113997                         });
113998                 }
113999             };
114000
114001             tool.uninstall = function() {
114002                 context.keybinding()
114003                     .off(commands[0].cmd)
114004                     .off(commands[1].cmd);
114005
114006                 context.map()
114007                     .on('move.undo_redo', null)
114008                     .on('drawn.undo_redo', null);
114009
114010                 context.history()
114011                     .on('change.undo_redo', null);
114012
114013                 context
114014                     .on('enter.undo_redo', null);
114015             };
114016
114017             return tool;
114018         }
114019
114020         function uiTopToolbar(context) {
114021
114022             var sidebarToggle = uiToolSidebarToggle(context),
114023                 modes = uiToolOldDrawModes(context),
114024                 notes = uiToolNotes(context),
114025                 undoRedo = uiToolUndoRedo(context),
114026                 save = uiToolSave(context);
114027
114028             function notesEnabled() {
114029                 var noteLayer = context.layers().layer('notes');
114030                 return noteLayer && noteLayer.enabled();
114031             }
114032
114033             function topToolbar(bar) {
114034
114035                 bar.on('wheel.topToolbar', function() {
114036                     if (!event.deltaX) {
114037                         // translate vertical scrolling into horizontal scrolling in case
114038                         // the user doesn't have an input device that can scroll horizontally
114039                         bar.node().scrollLeft += event.deltaY;
114040                     }
114041                 });
114042
114043                 var debouncedUpdate = debounce(update, 500, { leading: true, trailing: true });
114044                 context.layers()
114045                     .on('change.topToolbar', debouncedUpdate);
114046
114047                 update();
114048
114049                 function update() {
114050
114051                     var tools = [
114052                         sidebarToggle,
114053                         'spacer',
114054                         modes
114055                     ];
114056
114057                     tools.push('spacer');
114058
114059                     if (notesEnabled()) {
114060                         tools = tools.concat([notes, 'spacer']);
114061                     }
114062
114063                     tools = tools.concat([undoRedo, save]);
114064
114065                     var toolbarItems = bar.selectAll('.toolbar-item')
114066                         .data(tools, function(d) {
114067                             return d.id || d;
114068                         });
114069
114070                     toolbarItems.exit()
114071                         .each(function(d) {
114072                             if (d.uninstall) {
114073                                 d.uninstall();
114074                             }
114075                         })
114076                         .remove();
114077
114078                     var itemsEnter = toolbarItems
114079                         .enter()
114080                         .append('div')
114081                         .attr('class', function(d) {
114082                             var classes = 'toolbar-item ' + (d.id || d).replace('_', '-');
114083                             if (d.klass) { classes += ' ' + d.klass; }
114084                             return classes;
114085                         });
114086
114087                     var actionableItems = itemsEnter.filter(function(d) { return d !== 'spacer'; });
114088
114089                     actionableItems
114090                         .append('div')
114091                         .attr('class', 'item-content')
114092                         .each(function(d) {
114093                             select(this).call(d.render, bar);
114094                         });
114095
114096                     actionableItems
114097                         .append('div')
114098                         .attr('class', 'item-label')
114099                         .text(function(d) {
114100                             return d.label;
114101                         });
114102                 }
114103
114104             }
114105
114106             return topToolbar;
114107         }
114108
114109         // these are module variables so they are preserved through a ui.restart()
114110         var sawVersion = null;
114111         var isNewVersion = false;
114112         var isNewUser = false;
114113
114114
114115         function uiVersion(context) {
114116
114117             var currVersion = context.version;
114118             var matchedVersion = currVersion.match(/\d+\.\d+\.\d+.*/);
114119
114120             if (sawVersion === null && matchedVersion !== null) {
114121                 if (corePreferences('sawVersion')) {
114122                     isNewUser = false;
114123                     isNewVersion = corePreferences('sawVersion') !== currVersion && currVersion.indexOf('-') === -1;
114124                 } else {
114125                     isNewUser = true;
114126                     isNewVersion = true;
114127                 }
114128                 corePreferences('sawVersion', currVersion);
114129                 sawVersion = currVersion;
114130             }
114131
114132             return function(selection) {
114133                 selection
114134                     .append('a')
114135                     .attr('target', '_blank')
114136                     .attr('href', 'https://github.com/openstreetmap/iD')
114137                     .text(currVersion);
114138
114139                 // only show new version indicator to users that have used iD before
114140                 if (isNewVersion && !isNewUser) {
114141                     selection
114142                         .append('div')
114143                         .attr('class', 'badge')
114144                         .append('a')
114145                         .attr('target', '_blank')
114146                         .attr('href', 'https://github.com/openstreetmap/iD/blob/release/CHANGELOG.md#whats-new')
114147                         .call(svgIcon('#maki-gift-11'))
114148                         .call(uiTooltip()
114149                             .title(_t('version.whats_new', { version: currVersion }))
114150                             .placement('top')
114151                         );
114152                 }
114153             };
114154         }
114155
114156         function uiZoom(context) {
114157
114158             var zooms = [{
114159                 id: 'zoom-in',
114160                 icon: 'iD-icon-plus',
114161                 title: _t('zoom.in'),
114162                 action: zoomIn,
114163                 disabled: function() {
114164                     return !context.map().canZoomIn();
114165                 },
114166                 disabledTitle: _t('zoom.disabled.in'),
114167                 key: '+'
114168             }, {
114169                 id: 'zoom-out',
114170                 icon: 'iD-icon-minus',
114171                 title: _t('zoom.out'),
114172                 action: zoomOut,
114173                 disabled: function() {
114174                     return !context.map().canZoomOut();
114175                 },
114176                 disabledTitle: _t('zoom.disabled.out'),
114177                 key: '-'
114178             }];
114179
114180             function zoomIn() {
114181                 event.preventDefault();
114182                 context.map().zoomIn();
114183             }
114184
114185             function zoomOut() {
114186                 event.preventDefault();
114187                 context.map().zoomOut();
114188             }
114189
114190             function zoomInFurther() {
114191                 event.preventDefault();
114192                 context.map().zoomInFurther();
114193             }
114194
114195             function zoomOutFurther() {
114196                 event.preventDefault();
114197                 context.map().zoomOutFurther();
114198             }
114199
114200             return function(selection) {
114201                 var tooltipBehavior = uiTooltip()
114202                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114203                     .title(function(d) {
114204                         if (d.disabled()) {
114205                             return d.disabledTitle;
114206                         }
114207                         return d.title;
114208                     })
114209                     .keys(function(d) {
114210                         return [d.key];
114211                     });
114212
114213                 var lastPointerUpType;
114214
114215                 var buttons = selection.selectAll('button')
114216                     .data(zooms)
114217                     .enter()
114218                     .append('button')
114219                     .attr('class', function(d) { return d.id; })
114220                     .on('pointerup.editor', function() {
114221                         lastPointerUpType = event.pointerType;
114222                     })
114223                     .on('click.editor', function(d) {
114224                         if (!d.disabled()) {
114225                             d.action();
114226                         } else if (lastPointerUpType === 'touch' || lastPointerUpType === 'pen') {
114227                             context.ui().flash
114228                                 .duration(2000)
114229                                 .iconName('#' + d.icon)
114230                                 .iconClass('disabled')
114231                                 .text(d.disabledTitle)();
114232                         }
114233                         lastPointerUpType = null;
114234                     })
114235                     .call(tooltipBehavior);
114236
114237                 buttons.each(function(d) {
114238                     select(this)
114239                         .call(svgIcon('#' + d.icon, 'light'));
114240                 });
114241
114242                 ['plus', 'ffplus', '=', 'ffequals'].forEach(function(key) {
114243                     context.keybinding().on([key], zoomIn);
114244                     context.keybinding().on([uiCmd('⌘' + key)], zoomInFurther);
114245                 });
114246
114247                 ['_', '-', 'ffminus', 'dash'].forEach(function(key) {
114248                     context.keybinding().on([key], zoomOut);
114249                     context.keybinding().on([uiCmd('⌘' + key)], zoomOutFurther);
114250                 });
114251
114252                 function updateButtonStates() {
114253                     buttons
114254                         .classed('disabled', function(d) {
114255                             return d.disabled();
114256                         })
114257                         .each(function() {
114258                             var selection = select(this);
114259                             if (!selection.select('.tooltip.in').empty()) {
114260                                 selection.call(tooltipBehavior.updateContent);
114261                             }
114262                         });
114263                 }
114264
114265                 updateButtonStates();
114266
114267                 context.map().on('move.uiZoom', updateButtonStates);
114268             };
114269         }
114270
114271         function uiZoomToSelection(context) {
114272
114273             function isDisabled() {
114274                 var mode = context.mode();
114275                 return !mode || !mode.zoomToSelected;
114276             }
114277
114278             var _lastPointerUpType;
114279
114280             function pointerup() {
114281                 _lastPointerUpType = event.pointerType;
114282             }
114283
114284             function click() {
114285                 event.preventDefault();
114286
114287                 if (isDisabled()) {
114288                     if (_lastPointerUpType === 'touch' || _lastPointerUpType === 'pen') {
114289                         context.ui().flash
114290                             .duration(2000)
114291                             .iconName('#iD-icon-framed-dot')
114292                             .iconClass('disabled')
114293                             .text(_t('inspector.zoom_to.no_selection'))();
114294                     }
114295                 } else {
114296                     var mode = context.mode();
114297                     if (mode && mode.zoomToSelected) {
114298                         mode.zoomToSelected();
114299                     }
114300                 }
114301
114302                 _lastPointerUpType = null;
114303             }
114304
114305             return function(selection) {
114306
114307                 var tooltipBehavior = uiTooltip()
114308                     .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114309                     .title(function() {
114310                         if (isDisabled()) {
114311                             return _t('inspector.zoom_to.no_selection');
114312                         }
114313                         return _t('inspector.zoom_to.title');
114314                     })
114315                     .keys([_t('inspector.zoom_to.key')]);
114316
114317                 var button = selection
114318                     .append('button')
114319                     .on('pointerup', pointerup)
114320                     .on('click', click)
114321                     .call(svgIcon('#iD-icon-framed-dot', 'light'))
114322                     .call(tooltipBehavior);
114323
114324                 function setEnabledState() {
114325                     button.classed('disabled', isDisabled());
114326                     if (!button.select('.tooltip.in').empty()) {
114327                         button.call(tooltipBehavior.updateContent);
114328                     }
114329                 }
114330
114331                 context.on('enter.uiZoomToSelection', setEnabledState);
114332
114333                 setEnabledState();
114334             };
114335         }
114336
114337         function uiPane(id, context) {
114338
114339             var _key;
114340             var _title = '';
114341             var _description = '';
114342             var _iconName = '';
114343             var _sections; // array of uiSection objects
114344
114345             var _paneSelection = select(null);
114346
114347             var _paneTooltip;
114348
114349             var pane = {
114350                 id: id
114351             };
114352
114353             pane.title = function(val) {
114354                 if (!arguments.length) { return _title; }
114355                 _title = val;
114356                 return pane;
114357             };
114358
114359             pane.key = function(val) {
114360                 if (!arguments.length) { return _key; }
114361                 _key = val;
114362                 return pane;
114363             };
114364
114365             pane.description = function(val) {
114366                 if (!arguments.length) { return _description; }
114367                 _description = val;
114368                 return pane;
114369             };
114370
114371             pane.iconName = function(val) {
114372                 if (!arguments.length) { return _iconName; }
114373                 _iconName = val;
114374                 return pane;
114375             };
114376
114377             pane.sections = function(val) {
114378                 if (!arguments.length) { return _sections; }
114379                 _sections = val;
114380                 return pane;
114381             };
114382
114383             pane.selection = function() {
114384                 return _paneSelection;
114385             };
114386
114387             function hidePane() {
114388                 context.ui().togglePanes();
114389             }
114390
114391             pane.togglePane = function() {
114392                 if (event) { event.preventDefault(); }
114393                 _paneTooltip.hide();
114394                 context.ui().togglePanes(!_paneSelection.classed('shown') ? _paneSelection : undefined);
114395             };
114396
114397             pane.renderToggleButton = function(selection) {
114398
114399                 if (!_paneTooltip) {
114400                     _paneTooltip = uiTooltip()
114401                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114402                         .title(_description)
114403                         .keys([_key]);
114404                 }
114405
114406                 selection
114407                     .append('button')
114408                     .on('click', pane.togglePane)
114409                     .call(svgIcon('#' + _iconName, 'light'))
114410                     .call(_paneTooltip);
114411             };
114412
114413             pane.renderContent = function(selection) {
114414                 // override to fully customize content
114415
114416                 if (_sections) {
114417                     _sections.forEach(function(section) {
114418                         selection.call(section.render);
114419                     });
114420                 }
114421             };
114422
114423             pane.renderPane = function(selection) {
114424
114425                 _paneSelection = selection
114426                     .append('div')
114427                     .attr('class', 'fillL map-pane hide ' + id + '-pane')
114428                     .attr('pane', id);
114429
114430                 var heading = _paneSelection
114431                     .append('div')
114432                     .attr('class', 'pane-heading');
114433
114434                 heading
114435                     .append('h2')
114436                     .text(_title);
114437
114438                 heading
114439                     .append('button')
114440                     .on('click', hidePane)
114441                     .call(svgIcon('#iD-icon-close'));
114442
114443
114444                 _paneSelection
114445                     .append('div')
114446                     .attr('class', 'pane-content')
114447                     .call(pane.renderContent);
114448
114449                 if (_key) {
114450                     context.keybinding()
114451                         .on(_key, pane.togglePane);
114452                 }
114453             };
114454
114455             return pane;
114456         }
114457
114458         function uiSectionBackgroundDisplayOptions(context) {
114459
114460             var section = uiSection('background-display-options', context)
114461                 .title(_t('background.display_options'))
114462                 .disclosureContent(renderDisclosureContent);
114463
114464             var _detected = utilDetect();
114465             var _storedOpacity = corePreferences('background-opacity');
114466             var _minVal = 0.25;
114467             var _maxVal = _detected.cssfilters ? 2 : 1;
114468
114469             var _sliders = _detected.cssfilters
114470                 ? ['brightness', 'contrast', 'saturation', 'sharpness']
114471                 : ['brightness'];
114472
114473             var _options = {
114474                 brightness: (_storedOpacity !== null ? (+_storedOpacity) : 1),
114475                 contrast: 1,
114476                 saturation: 1,
114477                 sharpness: 1
114478             };
114479
114480             function clamp(x, min, max) {
114481                 return Math.max(min, Math.min(x, max));
114482             }
114483
114484             function updateValue(d, val) {
114485                 if (!val && event && event.target) {
114486                     val = event.target.value;
114487                 }
114488
114489                 val = clamp(val, _minVal, _maxVal);
114490
114491                 _options[d] = val;
114492                 context.background()[d](val);
114493
114494                 if (d === 'brightness') {
114495                     corePreferences('background-opacity', val);
114496                 }
114497
114498                 section.reRender();
114499             }
114500
114501             function renderDisclosureContent(selection) {
114502                 var container = selection.selectAll('.display-options-container')
114503                     .data([0]);
114504
114505                 var containerEnter = container.enter()
114506                     .append('div')
114507                     .attr('class', 'display-options-container controls-list');
114508
114509                 // add slider controls
114510                 var slidersEnter = containerEnter.selectAll('.display-control')
114511                     .data(_sliders)
114512                     .enter()
114513                     .append('div')
114514                     .attr('class', function(d) { return 'display-control display-control-' + d; });
114515
114516                 slidersEnter
114517                     .append('h5')
114518                     .text(function(d) { return _t('background.' + d); })
114519                     .append('span')
114520                     .attr('class', function(d) { return 'display-option-value display-option-value-' + d; });
114521
114522                 slidersEnter
114523                     .append('input')
114524                     .attr('class', function(d) { return 'display-option-input display-option-input-' + d; })
114525                     .attr('type', 'range')
114526                     .attr('min', _minVal)
114527                     .attr('max', _maxVal)
114528                     .attr('step', '0.05')
114529                     .on('input', function(d) {
114530                         var val = select(this).property('value');
114531                         updateValue(d, val);
114532                     });
114533
114534                 slidersEnter
114535                     .append('button')
114536                     .attr('title', _t('background.reset'))
114537                     .attr('class', function(d) { return 'display-option-reset display-option-reset-' + d; })
114538                     .on('click', function(d) {
114539                         if (event.button !== 0) { return; }
114540                         updateValue(d, 1);
114541                     })
114542                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
114543
114544                 // reset all button
114545                 containerEnter
114546                     .append('a')
114547                     .attr('class', 'display-option-resetlink')
114548                     .attr('href', '#')
114549                     .text(_t('background.reset_all'))
114550                     .on('click', function() {
114551                         for (var i = 0; i < _sliders.length; i++) {
114552                             updateValue(_sliders[i],1);
114553                         }
114554                     });
114555
114556                 // update
114557                 container = containerEnter
114558                     .merge(container);
114559
114560                 container.selectAll('.display-option-input')
114561                     .property('value', function(d) { return _options[d]; });
114562
114563                 container.selectAll('.display-option-value')
114564                     .text(function(d) { return Math.floor(_options[d] * 100) + '%'; });
114565
114566                 container.selectAll('.display-option-reset')
114567                     .classed('disabled', function(d) { return _options[d] === 1; });
114568
114569                 // first time only, set brightness if needed
114570                 if (containerEnter.size() && _options.brightness !== 1) {
114571                     context.background().brightness(_options.brightness);
114572                 }
114573             }
114574
114575             return section;
114576         }
114577
114578         function uiSettingsCustomBackground() {
114579             var dispatch$1 = dispatch('change');
114580
114581             function render(selection) {
114582                 // keep separate copies of original and current settings
114583                 var _origSettings = {
114584                     template: corePreferences('background-custom-template')
114585                 };
114586                 var _currSettings = {
114587                     template: corePreferences('background-custom-template')
114588                 };
114589
114590                 var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
114591                 var modal = uiConfirm(selection).okButton();
114592
114593                 modal
114594                     .classed('settings-modal settings-custom-background', true);
114595
114596                 modal.select('.modal-section.header')
114597                     .append('h3')
114598                     .text(_t('settings.custom_background.header'));
114599
114600
114601                 var textSection = modal.select('.modal-section.message-text');
114602
114603                 var instructions =
114604                     (_t('settings.custom_background.instructions.info')) + "\n" +
114605                     '\n' +
114606                     "#### " + (_t('settings.custom_background.instructions.wms.tokens_label')) + "\n" +
114607                     "* " + (_t('settings.custom_background.instructions.wms.tokens.proj')) + "\n" +
114608                     "* " + (_t('settings.custom_background.instructions.wms.tokens.wkid')) + "\n" +
114609                     "* " + (_t('settings.custom_background.instructions.wms.tokens.dimensions')) + "\n" +
114610                     "* " + (_t('settings.custom_background.instructions.wms.tokens.bbox')) + "\n" +
114611                     '\n' +
114612                     "#### " + (_t('settings.custom_background.instructions.tms.tokens_label')) + "\n" +
114613                     "* " + (_t('settings.custom_background.instructions.tms.tokens.xyz')) + "\n" +
114614                     "* " + (_t('settings.custom_background.instructions.tms.tokens.flipped_y')) + "\n" +
114615                     "* " + (_t('settings.custom_background.instructions.tms.tokens.switch')) + "\n" +
114616                     "* " + (_t('settings.custom_background.instructions.tms.tokens.quadtile')) + "\n" +
114617                     "* " + (_t('settings.custom_background.instructions.tms.tokens.scale_factor')) + "\n" +
114618                     '\n' +
114619                     "#### " + (_t('settings.custom_background.instructions.example')) + "\n" +
114620                     "`" + example + "`";
114621
114622                 textSection
114623                     .append('div')
114624                     .attr('class', 'instructions-template')
114625                     .html(marked_1(instructions));
114626
114627                 textSection
114628                     .append('textarea')
114629                     .attr('class', 'field-template')
114630                     .attr('placeholder', _t('settings.custom_background.template.placeholder'))
114631                     .call(utilNoAuto)
114632                     .property('value', _currSettings.template);
114633
114634
114635                 // insert a cancel button
114636                 var buttonSection = modal.select('.modal-section.buttons');
114637
114638                 buttonSection
114639                     .insert('button', '.ok-button')
114640                     .attr('class', 'button cancel-button secondary-action')
114641                     .text(_t('confirm.cancel'));
114642
114643
114644                 buttonSection.select('.cancel-button')
114645                     .on('click.cancel', clickCancel);
114646
114647                 buttonSection.select('.ok-button')
114648                     .attr('disabled', isSaveDisabled)
114649                     .on('click.save', clickSave);
114650
114651
114652                 function isSaveDisabled() {
114653                     return null;
114654                 }
114655
114656
114657                 // restore the original template
114658                 function clickCancel() {
114659                     textSection.select('.field-template').property('value', _origSettings.template);
114660                     corePreferences('background-custom-template', _origSettings.template);
114661                     this.blur();
114662                     modal.close();
114663                 }
114664
114665                 // accept the current template
114666                 function clickSave() {
114667                     _currSettings.template = textSection.select('.field-template').property('value');
114668                     corePreferences('background-custom-template', _currSettings.template);
114669                     this.blur();
114670                     modal.close();
114671                     dispatch$1.call('change', this, _currSettings);
114672                 }
114673             }
114674
114675             return utilRebind(render, dispatch$1, 'on');
114676         }
114677
114678         function uiSectionBackgroundList(context) {
114679
114680             var _backgroundList = select(null);
114681
114682             var _customSource = context.background().findSource('custom');
114683
114684             var _settingsCustomBackground = uiSettingsCustomBackground()
114685                 .on('change', customChanged);
114686
114687             var section = uiSection('background-list', context)
114688                 .title(_t('background.backgrounds'))
114689                 .disclosureContent(renderDisclosureContent);
114690
114691             function previousBackgroundID() {
114692                 return corePreferences('background-last-used-toggle');
114693             }
114694
114695             function renderDisclosureContent(selection) {
114696
114697                 // the background list
114698                 var container = selection.selectAll('.layer-background-list')
114699                     .data([0]);
114700
114701                 _backgroundList = container.enter()
114702                     .append('ul')
114703                     .attr('class', 'layer-list layer-background-list')
114704                     .attr('dir', 'auto')
114705                     .merge(container);
114706
114707
114708                 // add minimap toggle below list
114709                 var bgExtrasListEnter = selection.selectAll('.bg-extras-list')
114710                     .data([0])
114711                     .enter()
114712                     .append('ul')
114713                     .attr('class', 'layer-list bg-extras-list');
114714
114715                 var minimapLabelEnter = bgExtrasListEnter
114716                     .append('li')
114717                     .attr('class', 'minimap-toggle-item')
114718                     .append('label')
114719                     .call(uiTooltip()
114720                         .title(_t('background.minimap.tooltip'))
114721                         .keys([_t('background.minimap.key')])
114722                         .placement('top')
114723                     );
114724
114725                 minimapLabelEnter
114726                     .append('input')
114727                     .attr('type', 'checkbox')
114728                     .on('change', function() {
114729                         event.preventDefault();
114730                         uiMapInMap.toggle();
114731                     });
114732
114733                 minimapLabelEnter
114734                     .append('span')
114735                     .text(_t('background.minimap.description'));
114736
114737
114738                 var panelLabelEnter = bgExtrasListEnter
114739                     .append('li')
114740                     .attr('class', 'background-panel-toggle-item')
114741                     .append('label')
114742                     .call(uiTooltip()
114743                         .title(_t('background.panel.tooltip'))
114744                         .keys([uiCmd('⌘⇧' + _t('info_panels.background.key'))])
114745                         .placement('top')
114746                     );
114747
114748                 panelLabelEnter
114749                     .append('input')
114750                     .attr('type', 'checkbox')
114751                     .on('change', function() {
114752                         event.preventDefault();
114753                         context.ui().info.toggle('background');
114754                     });
114755
114756                 panelLabelEnter
114757                     .append('span')
114758                     .text(_t('background.panel.description'));
114759
114760                 var locPanelLabelEnter = bgExtrasListEnter
114761                     .append('li')
114762                     .attr('class', 'location-panel-toggle-item')
114763                     .append('label')
114764                     .call(uiTooltip()
114765                         .title(_t('background.location_panel.tooltip'))
114766                         .keys([uiCmd('⌘⇧' + _t('info_panels.location.key'))])
114767                         .placement('top')
114768                     );
114769
114770                 locPanelLabelEnter
114771                     .append('input')
114772                     .attr('type', 'checkbox')
114773                     .on('change', function() {
114774                         event.preventDefault();
114775                         context.ui().info.toggle('location');
114776                     });
114777
114778                 locPanelLabelEnter
114779                     .append('span')
114780                     .text(_t('background.location_panel.description'));
114781
114782
114783                 // "Info / Report a Problem" link
114784                 selection.selectAll('.imagery-faq')
114785                     .data([0])
114786                     .enter()
114787                     .append('div')
114788                     .attr('class', 'imagery-faq')
114789                     .append('a')
114790                     .attr('target', '_blank')
114791                     .call(svgIcon('#iD-icon-out-link', 'inline'))
114792                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/FAQ.md#how-can-i-report-an-issue-with-background-imagery')
114793                     .append('span')
114794                     .text(_t('background.imagery_problem_faq'));
114795
114796                 _backgroundList
114797                     .call(drawListItems, 'radio', chooseBackground, function(d) { return !d.isHidden() && !d.overlay; });
114798             }
114799
114800             function setTooltips(selection) {
114801                 selection.each(function(d, i, nodes) {
114802                     var item = select(this).select('label');
114803                     var span = item.select('span');
114804                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
114805                     var description = d.description();
114806                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
114807
114808                     item.call(uiTooltip().destroyAny);
114809
114810                     if (d.id === previousBackgroundID()) {
114811                         item.call(uiTooltip()
114812                             .placement(placement)
114813                             .title('<div>' + _t('background.switch') + '</div>')
114814                             .keys([uiCmd('⌘' + _t('background.key'))])
114815                         );
114816                     } else if (description || isOverflowing) {
114817                         item.call(uiTooltip()
114818                             .placement(placement)
114819                             .title(description || d.name())
114820                         );
114821                     }
114822                 });
114823             }
114824
114825             function drawListItems(layerList, type, change, filter) {
114826                 var sources = context.background()
114827                     .sources(context.map().extent(), context.map().zoom(), true)
114828                     .filter(filter);
114829
114830                 var layerLinks = layerList.selectAll('li')
114831                     .data(sources, function(d) { return d.name(); });
114832
114833                 layerLinks.exit()
114834                     .remove();
114835
114836                 var enter = layerLinks.enter()
114837                     .append('li')
114838                     .classed('layer-custom', function(d) { return d.id === 'custom'; })
114839                     .classed('best', function(d) { return d.best(); });
114840
114841                 var label = enter
114842                     .append('label');
114843
114844                 label
114845                     .append('input')
114846                     .attr('type', type)
114847                     .attr('name', 'layers')
114848                     .on('change', change);
114849
114850                 label
114851                     .append('span')
114852                     .text(function(d) { return d.name(); });
114853
114854                 enter.filter(function(d) { return d.id === 'custom'; })
114855                     .append('button')
114856                     .attr('class', 'layer-browse')
114857                     .call(uiTooltip()
114858                         .title(_t('settings.custom_background.tooltip'))
114859                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114860                     )
114861                     .on('click', editCustom)
114862                     .call(svgIcon('#iD-icon-more'));
114863
114864                 enter.filter(function(d) { return d.best(); })
114865                     .append('div')
114866                     .attr('class', 'best')
114867                     .call(uiTooltip()
114868                         .title(_t('background.best_imagery'))
114869                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
114870                     )
114871                     .append('span')
114872                     .html('&#9733;');
114873
114874
114875                 layerList.selectAll('li')
114876                     .sort(sortSources);
114877
114878                 layerList
114879                     .call(updateLayerSelections);
114880
114881
114882                 function sortSources(a, b) {
114883                     return a.best() && !b.best() ? -1
114884                         : b.best() && !a.best() ? 1
114885                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
114886                 }
114887             }
114888
114889             function updateLayerSelections(selection) {
114890                 function active(d) {
114891                     return context.background().showsLayer(d);
114892                 }
114893
114894                 selection.selectAll('li')
114895                     .classed('active', active)
114896                     .classed('switch', function(d) { return d.id === previousBackgroundID(); })
114897                     .call(setTooltips)
114898                     .selectAll('input')
114899                     .property('checked', active);
114900             }
114901
114902
114903             function chooseBackground(d) {
114904                 if (d.id === 'custom' && !d.template()) {
114905                     return editCustom();
114906                 }
114907
114908                 event.preventDefault();
114909                 var previousBackground = context.background().baseLayerSource();
114910                 corePreferences('background-last-used-toggle', previousBackground.id);
114911                 corePreferences('background-last-used', d.id);
114912                 context.background().baseLayerSource(d);
114913                 document.activeElement.blur();
114914             }
114915
114916
114917             function customChanged(d) {
114918                 if (d && d.template) {
114919                     _customSource.template(d.template);
114920                     chooseBackground(_customSource);
114921                 } else {
114922                     _customSource.template('');
114923                     chooseBackground(context.background().findSource('none'));
114924                 }
114925             }
114926
114927
114928             function editCustom() {
114929                 event.preventDefault();
114930                 context.container()
114931                     .call(_settingsCustomBackground);
114932             }
114933
114934
114935             context.background()
114936                 .on('change.background_list', function() {
114937                     _backgroundList.call(updateLayerSelections);
114938                 });
114939
114940             context.map()
114941                 .on('move.background_list',
114942                     debounce(function() {
114943                         // layers in-view may have changed due to map move
114944                         window.requestIdleCallback(section.reRender);
114945                     }, 1000)
114946                 );
114947
114948             return section;
114949         }
114950
114951         function uiSectionBackgroundOffset(context) {
114952
114953             var section = uiSection('background-offset', context)
114954                 .title(_t('background.fix_misalignment'))
114955                 .disclosureContent(renderDisclosureContent)
114956                 .expandedByDefault(false);
114957
114958             var _pointerPrefix = 'PointerEvent' in window ? 'pointer' : 'mouse';
114959
114960             var _directions = [
114961                 ['right', [0.5, 0]],
114962                 ['top', [0, -0.5]],
114963                 ['left', [-0.5, 0]],
114964                 ['bottom', [0, 0.5]]
114965             ];
114966
114967
114968             function cancelEvent() {
114969                 event.stopPropagation();
114970                 event.preventDefault();
114971             }
114972
114973
114974             function updateValue() {
114975                 var meters = geoOffsetToMeters(context.background().offset());
114976                 var x = +meters[0].toFixed(2);
114977                 var y = +meters[1].toFixed(2);
114978
114979                 context.container().selectAll('.nudge-inner-rect')
114980                     .select('input')
114981                     .classed('error', false)
114982                     .property('value', x + ', ' + y);
114983
114984                 context.container().selectAll('.nudge-reset')
114985                     .classed('disabled', function() {
114986                         return (x === 0 && y === 0);
114987                     });
114988             }
114989
114990
114991             function resetOffset() {
114992                 context.background().offset([0, 0]);
114993                 updateValue();
114994             }
114995
114996
114997             function nudge(d) {
114998                 context.background().nudge(d, context.map().zoom());
114999                 updateValue();
115000             }
115001
115002
115003             function pointerdownNudgeButton(d) {
115004                 var interval;
115005                 var timeout = window.setTimeout(function() {
115006                         interval = window.setInterval(nudge.bind(null, d), 100);
115007                     }, 500);
115008
115009                 function doneNudge() {
115010                     window.clearTimeout(timeout);
115011                     window.clearInterval(interval);
115012                     select(window)
115013                         .on(_pointerPrefix + 'up.buttonoffset', null, true)
115014                         .on(_pointerPrefix + 'down.buttonoffset', null, true);
115015                 }
115016
115017                 select(window)
115018                     .on(_pointerPrefix + 'up.buttonoffset', doneNudge, true)
115019                     .on(_pointerPrefix + 'down.buttonoffset', doneNudge, true);
115020
115021                 nudge(d);
115022             }
115023
115024
115025             function inputOffset() {
115026                 var input = select(this);
115027                 var d = input.node().value;
115028
115029                 if (d === '') { return resetOffset(); }
115030
115031                 d = d.replace(/;/g, ',').split(',').map(function(n) {
115032                     // if n is NaN, it will always get mapped to false.
115033                     return !isNaN(n) && n;
115034                 });
115035
115036                 if (d.length !== 2 || !d[0] || !d[1]) {
115037                     input.classed('error', true);
115038                     return;
115039                 }
115040
115041                 context.background().offset(geoMetersToOffset(d));
115042                 updateValue();
115043             }
115044
115045
115046             function dragOffset() {
115047                 if (event.button !== 0) { return; }
115048
115049                 var origin = [event.clientX, event.clientY];
115050
115051                 var pointerId = event.pointerId || 'mouse';
115052
115053                 context.container()
115054                     .append('div')
115055                     .attr('class', 'nudge-surface');
115056
115057                 select(window)
115058                     .on(_pointerPrefix + 'move.drag-bg-offset', pointermove)
115059                     .on(_pointerPrefix + 'up.drag-bg-offset', pointerup);
115060
115061                 if (_pointerPrefix === 'pointer') {
115062                     select(window)
115063                         .on('pointercancel.drag-bg-offset', pointerup);
115064                 }
115065
115066                 function pointermove() {
115067                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115068
115069                     var latest = [event.clientX, event.clientY];
115070                     var d = [
115071                         -(origin[0] - latest[0]) / 4,
115072                         -(origin[1] - latest[1]) / 4
115073                     ];
115074
115075                     origin = latest;
115076                     nudge(d);
115077                 }
115078
115079                 function pointerup() {
115080                     if (pointerId !== (event.pointerId || 'mouse')) { return; }
115081                     if (event.button !== 0) { return; }
115082
115083                     context.container().selectAll('.nudge-surface')
115084                         .remove();
115085
115086                     select(window)
115087                         .on('.drag-bg-offset', null);
115088                 }
115089             }
115090
115091
115092             function renderDisclosureContent(selection) {
115093                 var container = selection.selectAll('.nudge-container')
115094                     .data([0]);
115095
115096                 var containerEnter = container.enter()
115097                     .append('div')
115098                     .attr('class', 'nudge-container cf');
115099
115100                 containerEnter
115101                     .append('div')
115102                     .attr('class', 'nudge-instructions')
115103                     .text(_t('background.offset'));
115104
115105                 var nudgeEnter = containerEnter
115106                     .append('div')
115107                     .attr('class', 'nudge-outer-rect')
115108                     .on(_pointerPrefix + 'down', dragOffset);
115109
115110                 nudgeEnter
115111                     .append('div')
115112                     .attr('class', 'nudge-inner-rect')
115113                     .append('input')
115114                     .on('change', inputOffset);
115115
115116                 containerEnter
115117                     .append('div')
115118                     .selectAll('button')
115119                     .data(_directions).enter()
115120                     .append('button')
115121                     .attr('class', function(d) { return d[0] + ' nudge'; })
115122                     .on('contextmenu', cancelEvent)
115123                     .on(_pointerPrefix + 'down', function(d) {
115124                         if (event.button !== 0) { return; }
115125                         pointerdownNudgeButton(d[1]);
115126                     });
115127
115128                 containerEnter
115129                     .append('button')
115130                     .attr('title', _t('background.reset'))
115131                     .attr('class', 'nudge-reset disabled')
115132                     .on('contextmenu', cancelEvent)
115133                     .on('click', function() {
115134                         event.preventDefault();
115135                         if (event.button !== 0) { return; }
115136                         resetOffset();
115137                     })
115138                     .call(svgIcon('#iD-icon-' + (_mainLocalizer.textDirection() === 'rtl' ? 'redo' : 'undo')));
115139
115140                 updateValue();
115141             }
115142
115143             context.background()
115144                 .on('change.backgroundOffset-update', updateValue);
115145
115146             return section;
115147         }
115148
115149         function uiSectionOverlayList(context) {
115150
115151             var section = uiSection('overlay-list', context)
115152                 .title(_t('background.overlays'))
115153                 .disclosureContent(renderDisclosureContent);
115154
115155             var _overlayList = select(null);
115156
115157             function setTooltips(selection) {
115158                 selection.each(function(d, i, nodes) {
115159                     var item = select(this).select('label');
115160                     var span = item.select('span');
115161                     var placement = (i < nodes.length / 2) ? 'bottom' : 'top';
115162                     var description = d.description();
115163                     var isOverflowing = (span.property('clientWidth') !== span.property('scrollWidth'));
115164
115165                     item.call(uiTooltip().destroyAny);
115166
115167                     if (description || isOverflowing) {
115168                         item.call(uiTooltip()
115169                             .placement(placement)
115170                             .title(description || d.name())
115171                         );
115172                     }
115173                 });
115174             }
115175
115176             function updateLayerSelections(selection) {
115177                 function active(d) {
115178                     return context.background().showsLayer(d);
115179                 }
115180
115181                 selection.selectAll('li')
115182                     .classed('active', active)
115183                     .call(setTooltips)
115184                     .selectAll('input')
115185                     .property('checked', active);
115186             }
115187
115188
115189             function chooseOverlay(d) {
115190                 event.preventDefault();
115191                 context.background().toggleOverlayLayer(d);
115192                 _overlayList.call(updateLayerSelections);
115193                 document.activeElement.blur();
115194             }
115195
115196             function drawListItems(layerList, type, change, filter) {
115197                 var sources = context.background()
115198                     .sources(context.map().extent(), context.map().zoom(), true)
115199                     .filter(filter);
115200
115201                 var layerLinks = layerList.selectAll('li')
115202                     .data(sources, function(d) { return d.name(); });
115203
115204                 layerLinks.exit()
115205                     .remove();
115206
115207                 var enter = layerLinks.enter()
115208                     .append('li');
115209
115210                 var label = enter
115211                     .append('label');
115212
115213                 label
115214                     .append('input')
115215                     .attr('type', type)
115216                     .attr('name', 'layers')
115217                     .on('change', change);
115218
115219                 label
115220                     .append('span')
115221                     .text(function(d) { return d.name(); });
115222
115223
115224                 layerList.selectAll('li')
115225                     .sort(sortSources);
115226
115227                 layerList
115228                     .call(updateLayerSelections);
115229
115230
115231                 function sortSources(a, b) {
115232                     return a.best() && !b.best() ? -1
115233                         : b.best() && !a.best() ? 1
115234                         : d3_descending(a.area(), b.area()) || d3_ascending(a.name(), b.name()) || 0;
115235                 }
115236             }
115237
115238             function renderDisclosureContent(selection) {
115239
115240                 var container = selection.selectAll('.layer-overlay-list')
115241                     .data([0]);
115242
115243                 _overlayList = container.enter()
115244                     .append('ul')
115245                     .attr('class', 'layer-list layer-overlay-list')
115246                     .attr('dir', 'auto')
115247                     .merge(container);
115248
115249                 _overlayList
115250                     .call(drawListItems, 'checkbox', chooseOverlay, function(d) { return !d.isHidden() && d.overlay; });
115251             }
115252
115253             context.map()
115254                 .on('move.overlay_list',
115255                     debounce(function() {
115256                         // layers in-view may have changed due to map move
115257                         window.requestIdleCallback(section.reRender);
115258                     }, 1000)
115259                 );
115260
115261             return section;
115262         }
115263
115264         function uiPaneBackground(context) {
115265
115266             var backgroundPane = uiPane('background', context)
115267                 .key(_t('background.key'))
115268                 .title(_t('background.title'))
115269                 .description(_t('background.description'))
115270                 .iconName('iD-icon-layers')
115271                 .sections([
115272                     uiSectionBackgroundList(context),
115273                     uiSectionOverlayList(context),
115274                     uiSectionBackgroundDisplayOptions(context),
115275                     uiSectionBackgroundOffset(context)
115276                 ]);
115277
115278             return backgroundPane;
115279         }
115280
115281         function uiPaneHelp(context) {
115282
115283             var docKeys = [
115284                 ['help', [
115285                     'welcome',
115286                     'open_data_h',
115287                     'open_data',
115288                     'before_start_h',
115289                     'before_start',
115290                     'open_source_h',
115291                     'open_source',
115292                     'open_source_help'
115293                 ]],
115294                 ['overview', [
115295                     'navigation_h',
115296                     'navigation_drag',
115297                     'navigation_zoom',
115298                     'features_h',
115299                     'features',
115300                     'nodes_ways'
115301                 ]],
115302                 ['editing', [
115303                     'select_h',
115304                     'select_left_click',
115305                     'select_right_click',
115306                     'select_space',
115307                     'multiselect_h',
115308                     'multiselect',
115309                     'multiselect_shift_click',
115310                     'multiselect_lasso',
115311                     'undo_redo_h',
115312                     'undo_redo',
115313                     'save_h',
115314                     'save',
115315                     'save_validation',
115316                     'upload_h',
115317                     'upload',
115318                     'backups_h',
115319                     'backups',
115320                     'keyboard_h',
115321                     'keyboard'
115322                 ]],
115323                 ['feature_editor', [
115324                     'intro',
115325                     'definitions',
115326                     'type_h',
115327                     'type',
115328                     'type_picker',
115329                     'fields_h',
115330                     'fields_all_fields',
115331                     'fields_example',
115332                     'fields_add_field',
115333                     'tags_h',
115334                     'tags_all_tags',
115335                     'tags_resources'
115336                 ]],
115337                 ['points', [
115338                     'intro',
115339                     'add_point_h',
115340                     'add_point',
115341                     'add_point_finish',
115342                     'move_point_h',
115343                     'move_point',
115344                     'delete_point_h',
115345                     'delete_point',
115346                     'delete_point_command'
115347                 ]],
115348                 ['lines', [
115349                     'intro',
115350                     'add_line_h',
115351                     'add_line',
115352                     'add_line_draw',
115353                     'add_line_continue',
115354                     'add_line_finish',
115355                     'modify_line_h',
115356                     'modify_line_dragnode',
115357                     'modify_line_addnode',
115358                     'connect_line_h',
115359                     'connect_line',
115360                     'connect_line_display',
115361                     'connect_line_drag',
115362                     'connect_line_tag',
115363                     'disconnect_line_h',
115364                     'disconnect_line_command',
115365                     'move_line_h',
115366                     'move_line_command',
115367                     'move_line_connected',
115368                     'delete_line_h',
115369                     'delete_line',
115370                     'delete_line_command'
115371                 ]],
115372                 ['areas', [
115373                     'intro',
115374                     'point_or_area_h',
115375                     'point_or_area',
115376                     'add_area_h',
115377                     'add_area_command',
115378                     'add_area_draw',
115379                     'add_area_continue',
115380                     'add_area_finish',
115381                     'square_area_h',
115382                     'square_area_command',
115383                     'modify_area_h',
115384                     'modify_area_dragnode',
115385                     'modify_area_addnode',
115386                     'delete_area_h',
115387                     'delete_area',
115388                     'delete_area_command'
115389                 ]],
115390                 ['relations', [
115391                     'intro',
115392                     'edit_relation_h',
115393                     'edit_relation',
115394                     'edit_relation_add',
115395                     'edit_relation_delete',
115396                     'maintain_relation_h',
115397                     'maintain_relation',
115398                     'relation_types_h',
115399                     'multipolygon_h',
115400                     'multipolygon',
115401                     'multipolygon_create',
115402                     'multipolygon_merge',
115403                     'turn_restriction_h',
115404                     'turn_restriction',
115405                     'turn_restriction_field',
115406                     'turn_restriction_editing',
115407                     'route_h',
115408                     'route',
115409                     'route_add',
115410                     'boundary_h',
115411                     'boundary',
115412                     'boundary_add'
115413                 ]],
115414                 ['notes', [
115415                     'intro',
115416                     'add_note_h',
115417                     'add_note',
115418                     'place_note',
115419                     'move_note',
115420                     'update_note_h',
115421                     'update_note',
115422                     'save_note_h',
115423                     'save_note'
115424                 ]],
115425                 ['imagery', [
115426                     'intro',
115427                     'sources_h',
115428                     'choosing',
115429                     'sources',
115430                     'offsets_h',
115431                     'offset',
115432                     'offset_change'
115433                 ]],
115434                 ['streetlevel', [
115435                     'intro',
115436                     'using_h',
115437                     'using',
115438                     'photos',
115439                     'viewer'
115440                 ]],
115441                 ['gps', [
115442                     'intro',
115443                     'survey',
115444                     'using_h',
115445                     'using',
115446                     'tracing',
115447                     'upload'
115448                 ]],
115449                 ['qa', [
115450                     'intro',
115451                     'tools_h',
115452                     'tools',
115453                     'issues_h',
115454                     'issues'
115455                 ]]
115456             ];
115457
115458             var headings = {
115459                 'help.help.open_data_h': 3,
115460                 'help.help.before_start_h': 3,
115461                 'help.help.open_source_h': 3,
115462                 'help.overview.navigation_h': 3,
115463                 'help.overview.features_h': 3,
115464                 'help.editing.select_h': 3,
115465                 'help.editing.multiselect_h': 3,
115466                 'help.editing.undo_redo_h': 3,
115467                 'help.editing.save_h': 3,
115468                 'help.editing.upload_h': 3,
115469                 'help.editing.backups_h': 3,
115470                 'help.editing.keyboard_h': 3,
115471                 'help.feature_editor.type_h': 3,
115472                 'help.feature_editor.fields_h': 3,
115473                 'help.feature_editor.tags_h': 3,
115474                 'help.points.add_point_h': 3,
115475                 'help.points.move_point_h': 3,
115476                 'help.points.delete_point_h': 3,
115477                 'help.lines.add_line_h': 3,
115478                 'help.lines.modify_line_h': 3,
115479                 'help.lines.connect_line_h': 3,
115480                 'help.lines.disconnect_line_h': 3,
115481                 'help.lines.move_line_h': 3,
115482                 'help.lines.delete_line_h': 3,
115483                 'help.areas.point_or_area_h': 3,
115484                 'help.areas.add_area_h': 3,
115485                 'help.areas.square_area_h': 3,
115486                 'help.areas.modify_area_h': 3,
115487                 'help.areas.delete_area_h': 3,
115488                 'help.relations.edit_relation_h': 3,
115489                 'help.relations.maintain_relation_h': 3,
115490                 'help.relations.relation_types_h': 2,
115491                 'help.relations.multipolygon_h': 3,
115492                 'help.relations.turn_restriction_h': 3,
115493                 'help.relations.route_h': 3,
115494                 'help.relations.boundary_h': 3,
115495                 'help.notes.add_note_h': 3,
115496                 'help.notes.update_note_h': 3,
115497                 'help.notes.save_note_h': 3,
115498                 'help.imagery.sources_h': 3,
115499                 'help.imagery.offsets_h': 3,
115500                 'help.streetlevel.using_h': 3,
115501                 'help.gps.using_h': 3,
115502                 'help.qa.tools_h': 3,
115503                 'help.qa.issues_h': 3
115504             };
115505
115506             // For each section, squash all the texts into a single markdown document
115507             var docs = docKeys.map(function(key) {
115508                 var helpkey = 'help.' + key[0];
115509                 var helpPaneReplacements = { version: context.version };
115510                 var text = key[1].reduce(function(all, part) {
115511                     var subkey = helpkey + '.' + part;
115512                     var depth = headings[subkey];                              // is this subkey a heading?
115513                     var hhh = depth ? Array(depth + 1).join('#') + ' ' : '';   // if so, prepend with some ##'s
115514                     return all + hhh + helpString(subkey, helpPaneReplacements) + '\n\n';
115515                 }, '');
115516
115517                 return {
115518                     title: _t(helpkey + '.title'),
115519                     html: marked_1(text.trim())
115520                         // use keyboard key styling for shortcuts
115521                         .replace(/<code>/g, '<kbd>')
115522                         .replace(/<\/code>/g, '<\/kbd>')
115523                 };
115524             });
115525
115526             var helpPane = uiPane('help', context)
115527                 .key(_t('help.key'))
115528                 .title(_t('help.title'))
115529                 .description(_t('help.title'))
115530                 .iconName('iD-icon-help');
115531
115532             helpPane.renderContent = function(content) {
115533
115534                 function clickHelp(d, i) {
115535                     var rtl = (_mainLocalizer.textDirection() === 'rtl');
115536                     content.property('scrollTop', 0);
115537                     helpPane.selection().select('.pane-heading h2').html(d.title);
115538
115539                     body.html(d.html);
115540                     body.selectAll('a')
115541                         .attr('target', '_blank');
115542                     menuItems.classed('selected', function(m) {
115543                         return m.title === d.title;
115544                     });
115545
115546                     nav.html('');
115547                     if (rtl) {
115548                         nav.call(drawNext).call(drawPrevious);
115549                     } else {
115550                         nav.call(drawPrevious).call(drawNext);
115551                     }
115552
115553
115554                     function drawNext(selection) {
115555                         if (i < docs.length - 1) {
115556                             var nextLink = selection
115557                                 .append('a')
115558                                 .attr('class', 'next')
115559                                 .on('click', function() {
115560                                     clickHelp(docs[i + 1], i + 1);
115561                                 });
115562
115563                             nextLink
115564                                 .append('span')
115565                                 .text(docs[i + 1].title)
115566                                 .call(svgIcon((rtl ? '#iD-icon-backward' : '#iD-icon-forward'), 'inline'));
115567                         }
115568                     }
115569
115570
115571                     function drawPrevious(selection) {
115572                         if (i > 0) {
115573                             var prevLink = selection
115574                                 .append('a')
115575                                 .attr('class', 'previous')
115576                                 .on('click', function() {
115577                                     clickHelp(docs[i - 1], i - 1);
115578                                 });
115579
115580                             prevLink
115581                                 .call(svgIcon((rtl ? '#iD-icon-forward' : '#iD-icon-backward'), 'inline'))
115582                                 .append('span')
115583                                 .text(docs[i - 1].title);
115584                         }
115585                     }
115586                 }
115587
115588
115589                 function clickWalkthrough() {
115590                     if (context.inIntro()) { return; }
115591                     context.container().call(uiIntro(context));
115592                     context.ui().togglePanes();
115593                 }
115594
115595
115596                 function clickShortcuts() {
115597                     context.container().call(uiShortcuts(context), true);
115598                 }
115599
115600                 var toc = content
115601                     .append('ul')
115602                     .attr('class', 'toc');
115603
115604                 var menuItems = toc.selectAll('li')
115605                     .data(docs)
115606                     .enter()
115607                     .append('li')
115608                     .append('a')
115609                     .html(function(d) { return d.title; })
115610                     .on('click', clickHelp);
115611
115612                 var shortcuts = toc
115613                     .append('li')
115614                     .attr('class', 'shortcuts')
115615                     .call(uiTooltip()
115616                         .title(_t('shortcuts.tooltip'))
115617                         .keys(['?'])
115618                         .placement('top')
115619                     )
115620                     .append('a')
115621                     .on('click', clickShortcuts);
115622
115623                 shortcuts
115624                     .append('div')
115625                     .text(_t('shortcuts.title'));
115626
115627                 var walkthrough = toc
115628                     .append('li')
115629                     .attr('class', 'walkthrough')
115630                     .append('a')
115631                     .on('click', clickWalkthrough);
115632
115633                 walkthrough
115634                     .append('svg')
115635                     .attr('class', 'logo logo-walkthrough')
115636                     .append('use')
115637                     .attr('xlink:href', '#iD-logo-walkthrough');
115638
115639                 walkthrough
115640                     .append('div')
115641                     .text(_t('splash.walkthrough'));
115642
115643
115644                 var helpContent = content
115645                     .append('div')
115646                     .attr('class', 'left-content');
115647
115648                 var body = helpContent
115649                     .append('div')
115650                     .attr('class', 'body');
115651
115652                 var nav = helpContent
115653                     .append('div')
115654                     .attr('class', 'nav');
115655
115656                 clickHelp(docs[0], 0);
115657
115658             };
115659
115660             return helpPane;
115661         }
115662
115663         function uiSectionValidationIssues(id, severity, context) {
115664
115665             var _issues = [];
115666
115667             var section = uiSection(id, context)
115668                 .title(function() {
115669                     if (!_issues) { return ''; }
115670                     var issueCountText = _issues.length > 1000 ? '1000+' : String(_issues.length);
115671                     return _t('issues.' + severity + 's.list_title', { count: issueCountText });
115672                 })
115673                 .disclosureContent(renderDisclosureContent)
115674                 .shouldDisplay(function() {
115675                     return _issues && _issues.length;
115676                 });
115677
115678             function getOptions() {
115679                 return {
115680                     what: corePreferences('validate-what') || 'edited',
115681                     where: corePreferences('validate-where') || 'all'
115682                 };
115683             }
115684
115685             // get and cache the issues to display, unordered
115686             function reloadIssues() {
115687                 _issues = context.validator().getIssuesBySeverity(getOptions())[severity];
115688             }
115689
115690             function renderDisclosureContent(selection) {
115691
115692                 var center = context.map().center();
115693                 var graph = context.graph();
115694
115695                 // sort issues by distance away from the center of the map
115696                 var issues = _issues.map(function withDistance(issue) {
115697                         var extent = issue.extent(graph);
115698                         var dist = extent ? geoSphericalDistance(center, extent.center()) : 0;
115699                         return Object.assign(issue, { dist: dist });
115700                     })
115701                     .sort(function byDistance(a, b) {
115702                         return a.dist - b.dist;
115703                     });
115704
115705                 // cut off at 1000
115706                 issues = issues.slice(0, 1000);
115707
115708                 //renderIgnoredIssuesReset(_warningsSelection);
115709
115710                 selection
115711                     .call(drawIssuesList, issues);
115712             }
115713
115714             function drawIssuesList(selection, issues) {
115715                 var list = selection.selectAll('.issues-list')
115716                     .data([0]);
115717
115718                 list = list.enter()
115719                     .append('ul')
115720                     .attr('class', 'layer-list issues-list ' + severity + 's-list')
115721                     .merge(list);
115722
115723
115724                 var items = list.selectAll('li')
115725                     .data(issues, function(d) { return d.id; });
115726
115727                 // Exit
115728                 items.exit()
115729                     .remove();
115730
115731                 // Enter
115732                 var itemsEnter = items.enter()
115733                     .append('li')
115734                     .attr('class', function (d) { return 'issue severity-' + d.severity; })
115735                     .on('click', function(d) {
115736                         context.validator().focusIssue(d);
115737                     })
115738                     .on('mouseover', function(d) {
115739                         utilHighlightEntities(d.entityIds, true, context);
115740                     })
115741                     .on('mouseout', function(d) {
115742                         utilHighlightEntities(d.entityIds, false, context);
115743                     });
115744
115745
115746                 var labelsEnter = itemsEnter
115747                     .append('div')
115748                     .attr('class', 'issue-label');
115749
115750                 var textEnter = labelsEnter
115751                     .append('span')
115752                     .attr('class', 'issue-text');
115753
115754                 textEnter
115755                     .append('span')
115756                     .attr('class', 'issue-icon')
115757                     .each(function(d) {
115758                         var iconName = '#iD-icon-' + (d.severity === 'warning' ? 'alert' : 'error');
115759                         select(this)
115760                             .call(svgIcon(iconName));
115761                     });
115762
115763                 textEnter
115764                     .append('span')
115765                     .attr('class', 'issue-message');
115766
115767                 /*
115768                 labelsEnter
115769                     .append('span')
115770                     .attr('class', 'issue-autofix')
115771                     .each(function(d) {
115772                         if (!d.autoFix) return;
115773
115774                         d3_select(this)
115775                             .append('button')
115776                             .attr('title', t('issues.fix_one.title'))
115777                             .datum(d.autoFix)  // set button datum to the autofix
115778                             .attr('class', 'autofix action')
115779                             .on('click', function(d) {
115780                                 d3_event.preventDefault();
115781                                 d3_event.stopPropagation();
115782
115783                                 var issuesEntityIDs = d.issue.entityIds;
115784                                 utilHighlightEntities(issuesEntityIDs.concat(d.entityIds), false, context);
115785
115786                                 context.perform.apply(context, d.autoArgs);
115787                                 context.validator().validate();
115788                             })
115789                             .call(svgIcon('#iD-icon-wrench'));
115790                     });
115791                 */
115792
115793                 // Update
115794                 items = items
115795                     .merge(itemsEnter)
115796                     .order();
115797
115798                 items.selectAll('.issue-message')
115799                     .text(function(d) {
115800                         return d.message(context);
115801                     });
115802
115803                 /*
115804                 // autofix
115805                 var canAutoFix = issues.filter(function(issue) { return issue.autoFix; });
115806
115807                 var autoFixAll = selection.selectAll('.autofix-all')
115808                     .data(canAutoFix.length ? [0] : []);
115809
115810                 // exit
115811                 autoFixAll.exit()
115812                     .remove();
115813
115814                 // enter
115815                 var autoFixAllEnter = autoFixAll.enter()
115816                     .insert('div', '.issues-list')
115817                     .attr('class', 'autofix-all');
115818
115819                 var linkEnter = autoFixAllEnter
115820                     .append('a')
115821                     .attr('class', 'autofix-all-link')
115822                     .attr('href', '#');
115823
115824                 linkEnter
115825                     .append('span')
115826                     .attr('class', 'autofix-all-link-text')
115827                     .text(t('issues.fix_all.title'));
115828
115829                 linkEnter
115830                     .append('span')
115831                     .attr('class', 'autofix-all-link-icon')
115832                     .call(svgIcon('#iD-icon-wrench'));
115833
115834                 if (severity === 'warning') {
115835                     renderIgnoredIssuesReset(selection);
115836                 }
115837
115838                 // update
115839                 autoFixAll = autoFixAll
115840                     .merge(autoFixAllEnter);
115841
115842                 autoFixAll.selectAll('.autofix-all-link')
115843                     .on('click', function() {
115844                         context.pauseChangeDispatch();
115845                         context.perform(actionNoop());
115846                         canAutoFix.forEach(function(issue) {
115847                             var args = issue.autoFix.autoArgs.slice();  // copy
115848                             if (typeof args[args.length - 1] !== 'function') {
115849                                 args.pop();
115850                             }
115851                             args.push(t('issues.fix_all.annotation'));
115852                             context.replace.apply(context, args);
115853                         });
115854                         context.resumeChangeDispatch();
115855                         context.validator().validate();
115856                     });
115857                 */
115858             }
115859
115860             context.validator().on('validated.uiSectionValidationIssues' + id, function() {
115861                 window.requestIdleCallback(function() {
115862                     reloadIssues();
115863                     section.reRender();
115864                 });
115865             });
115866
115867             context.map().on('move.uiSectionValidationIssues' + id,
115868                 debounce(function() {
115869                     window.requestIdleCallback(function() {
115870                         if (getOptions().where === 'visible') {
115871                             // must refetch issues if they are viewport-dependent
115872                             reloadIssues();
115873                         }
115874                         // always reload list to re-sort-by-distance
115875                         section.reRender();
115876                     });
115877                 }, 1000)
115878             );
115879
115880             return section;
115881         }
115882
115883         function uiSectionValidationOptions(context) {
115884
115885             var section = uiSection('issues-options', context)
115886                 .content(renderContent);
115887
115888             function renderContent(selection) {
115889
115890                 var container = selection.selectAll('.issues-options-container')
115891                     .data([0]);
115892
115893                 container = container.enter()
115894                     .append('div')
115895                     .attr('class', 'issues-options-container')
115896                     .merge(container);
115897
115898                 var data = [
115899                     { key: 'what', values: ['edited', 'all'] },
115900                     { key: 'where', values: ['visible', 'all'] }
115901                 ];
115902
115903                 var options = container.selectAll('.issues-option')
115904                     .data(data, function(d) { return d.key; });
115905
115906                 var optionsEnter = options.enter()
115907                     .append('div')
115908                     .attr('class', function(d) { return 'issues-option issues-option-' + d.key; });
115909
115910                 optionsEnter
115911                     .append('div')
115912                     .attr('class', 'issues-option-title')
115913                     .text(function(d) { return _t('issues.options.' + d.key + '.title'); });
115914
115915                 var valuesEnter = optionsEnter.selectAll('label')
115916                     .data(function(d) {
115917                         return d.values.map(function(val) { return { value: val, key: d.key }; });
115918                     })
115919                     .enter()
115920                     .append('label');
115921
115922                 valuesEnter
115923                     .append('input')
115924                     .attr('type', 'radio')
115925                     .attr('name', function(d) { return 'issues-option-' + d.key; })
115926                     .attr('value', function(d) { return d.value; })
115927                     .property('checked', function(d) { return getOptions()[d.key] === d.value; })
115928                     .on('change', function(d) { updateOptionValue(d.key, d.value); });
115929
115930                 valuesEnter
115931                     .append('span')
115932                     .text(function(d) { return _t('issues.options.' + d.key + '.' + d.value); });
115933             }
115934
115935             function getOptions() {
115936                 return {
115937                     what: corePreferences('validate-what') || 'edited',  // 'all', 'edited'
115938                     where: corePreferences('validate-where') || 'all'    // 'all', 'visible'
115939                 };
115940             }
115941
115942             function updateOptionValue(d, val) {
115943                 if (!val && event && event.target) {
115944                     val = event.target.value;
115945                 }
115946
115947                 corePreferences('validate-' + d, val);
115948                 context.validator().validate();
115949             }
115950
115951             return section;
115952         }
115953
115954         function uiSectionValidationRules(context) {
115955
115956             var MINSQUARE = 0;
115957             var MAXSQUARE = 20;
115958             var DEFAULTSQUARE = 5;  // see also unsquare_way.js
115959
115960             var section = uiSection('issues-rules', context)
115961                 .disclosureContent(renderDisclosureContent)
115962                 .title(_t('issues.rules.title'));
115963
115964             var _ruleKeys = context.validator().getRuleKeys()
115965                 .filter(function(key) { return key !== 'maprules'; })
115966                 .sort(function(key1, key2) {
115967                     // alphabetize by localized title
115968                     return _t('issues.' + key1 + '.title') < _t('issues.' + key2 + '.title') ? -1 : 1;
115969                 });
115970
115971             function renderDisclosureContent(selection) {
115972                 var container = selection.selectAll('.issues-rulelist-container')
115973                     .data([0]);
115974
115975                 var containerEnter = container.enter()
115976                     .append('div')
115977                     .attr('class', 'issues-rulelist-container');
115978
115979                 containerEnter
115980                     .append('ul')
115981                     .attr('class', 'layer-list issue-rules-list');
115982
115983                 var ruleLinks = containerEnter
115984                     .append('div')
115985                     .attr('class', 'issue-rules-links section-footer');
115986
115987                 ruleLinks
115988                     .append('a')
115989                     .attr('class', 'issue-rules-link')
115990                     .attr('href', '#')
115991                     .text(_t('issues.enable_all'))
115992                     .on('click', function() {
115993                         context.validator().disableRules([]);
115994                     });
115995
115996                 ruleLinks
115997                     .append('a')
115998                     .attr('class', 'issue-rules-link')
115999                     .attr('href', '#')
116000                     .text(_t('issues.disable_all'))
116001                     .on('click', function() {
116002                         context.validator().disableRules(_ruleKeys);
116003                     });
116004
116005
116006                 // Update
116007                 container = container
116008                     .merge(containerEnter);
116009
116010                 container.selectAll('.issue-rules-list')
116011                     .call(drawListItems, _ruleKeys, 'checkbox', 'rule', toggleRule, isRuleEnabled);
116012             }
116013
116014             function drawListItems(selection, data, type, name, change, active) {
116015                 var items = selection.selectAll('li')
116016                     .data(data);
116017
116018                 // Exit
116019                 items.exit()
116020                     .remove();
116021
116022                 // Enter
116023                 var enter = items.enter()
116024                     .append('li');
116025
116026                 if (name === 'rule') {
116027                     enter
116028                         .call(uiTooltip()
116029                             .title(function(d) { return _t('issues.' + d + '.tip'); })
116030                             .placement('top')
116031                         );
116032                 }
116033
116034                 var label = enter
116035                     .append('label');
116036
116037                 label
116038                     .append('input')
116039                     .attr('type', type)
116040                     .attr('name', name)
116041                     .on('change', change);
116042
116043                 label
116044                     .append('span')
116045                     .html(function(d) {
116046                         var params = {};
116047                         if (d === 'unsquare_way') {
116048                             params.val = '<span class="square-degrees"></span>';
116049                         }
116050                         return _t('issues.' + d + '.title', params);
116051                     });
116052
116053                 // Update
116054                 items = items
116055                     .merge(enter);
116056
116057                 items
116058                     .classed('active', active)
116059                     .selectAll('input')
116060                     .property('checked', active)
116061                     .property('indeterminate', false);
116062
116063
116064                 // user-configurable square threshold
116065                 var degStr = corePreferences('validate-square-degrees');
116066                 if (degStr === null) {
116067                     degStr = '' + DEFAULTSQUARE;
116068                 }
116069
116070                 var span = items.selectAll('.square-degrees');
116071                 var input = span.selectAll('.square-degrees-input')
116072                     .data([0]);
116073
116074                 // enter / update
116075                 input.enter()
116076                     .append('input')
116077                     .attr('type', 'number')
116078                     .attr('min', '' + MINSQUARE)
116079                     .attr('max', '' + MAXSQUARE)
116080                     .attr('step', '0.5')
116081                     .attr('class', 'square-degrees-input')
116082                     .call(utilNoAuto)
116083                     .on('click', function () {
116084                         event.preventDefault();
116085                         event.stopPropagation();
116086                         this.select();
116087                     })
116088                     .on('keyup', function () {
116089                         if (event.keyCode === 13) { // enter
116090                             this.blur();
116091                             this.select();
116092                         }
116093                     })
116094                     .on('blur', changeSquare)
116095                     .merge(input)
116096                     .property('value', degStr);
116097             }
116098
116099             function changeSquare() {
116100                 var input = select(this);
116101                 var degStr = utilGetSetValue(input).trim();
116102                 var degNum = parseFloat(degStr, 10);
116103
116104                 if (!isFinite(degNum)) {
116105                     degNum = DEFAULTSQUARE;
116106                 } else if (degNum > MAXSQUARE) {
116107                     degNum = MAXSQUARE;
116108                 } else if (degNum < MINSQUARE) {
116109                     degNum = MINSQUARE;
116110                 }
116111
116112                 degNum = Math.round(degNum * 10 ) / 10;   // round to 1 decimal
116113                 degStr = '' + degNum;
116114
116115                 input
116116                     .property('value', degStr);
116117
116118                 corePreferences('validate-square-degrees', degStr);
116119                 context.validator().reloadUnsquareIssues();
116120             }
116121
116122             function isRuleEnabled(d) {
116123                 return context.validator().isRuleEnabled(d);
116124             }
116125
116126             function toggleRule(d) {
116127                 context.validator().toggleRule(d);
116128             }
116129
116130             context.validator().on('validated.uiSectionValidationRules', function() {
116131                 window.requestIdleCallback(section.reRender);
116132             });
116133
116134             return section;
116135         }
116136
116137         function uiSectionValidationStatus(context) {
116138
116139             var section = uiSection('issues-status', context)
116140                 .content(renderContent)
116141                 .shouldDisplay(function() {
116142                     var issues = context.validator().getIssues(getOptions());
116143                     return issues.length === 0;
116144                 });
116145
116146             function getOptions() {
116147                 return {
116148                     what: corePreferences('validate-what') || 'edited',
116149                     where: corePreferences('validate-where') || 'all'
116150                 };
116151             }
116152
116153             function renderContent(selection) {
116154
116155                 var box = selection.selectAll('.box')
116156                     .data([0]);
116157
116158                 var boxEnter = box.enter()
116159                     .append('div')
116160                     .attr('class', 'box');
116161
116162                 boxEnter
116163                     .append('div')
116164                     .call(svgIcon('#iD-icon-apply', 'pre-text'));
116165
116166                 var noIssuesMessage = boxEnter
116167                     .append('span');
116168
116169                 noIssuesMessage
116170                     .append('strong')
116171                     .attr('class', 'message');
116172
116173                 noIssuesMessage
116174                     .append('br');
116175
116176                 noIssuesMessage
116177                     .append('span')
116178                     .attr('class', 'details');
116179
116180                 renderIgnoredIssuesReset(selection);
116181                 setNoIssuesText(selection);
116182             }
116183
116184             function renderIgnoredIssuesReset(selection) {
116185
116186                 var ignoredIssues = context.validator()
116187                     .getIssues({ what: 'all', where: 'all', includeDisabledRules: true, includeIgnored: 'only' });
116188
116189                 var resetIgnored = selection.selectAll('.reset-ignored')
116190                     .data(ignoredIssues.length ? [0] : []);
116191
116192                 // exit
116193                 resetIgnored.exit()
116194                     .remove();
116195
116196                 // enter
116197                 var resetIgnoredEnter = resetIgnored.enter()
116198                     .append('div')
116199                     .attr('class', 'reset-ignored section-footer');
116200
116201                 resetIgnoredEnter
116202                     .append('a')
116203                     .attr('href', '#');
116204
116205                 // update
116206                 resetIgnored = resetIgnored
116207                     .merge(resetIgnoredEnter);
116208
116209                 resetIgnored.select('a')
116210                     .text(_t('issues.reset_ignored', { count: ignoredIssues.length.toString() }));
116211
116212                 resetIgnored.on('click', function() {
116213                     context.validator().resetIgnoredIssues();
116214                 });
116215             }
116216
116217             function setNoIssuesText(selection) {
116218
116219                 var opts = getOptions();
116220
116221                 function checkForHiddenIssues(cases) {
116222                     for (var type in cases) {
116223                         var hiddenOpts = cases[type];
116224                         var hiddenIssues = context.validator().getIssues(hiddenOpts);
116225                         if (hiddenIssues.length) {
116226                             selection.select('.box .details')
116227                                 .text(_t(
116228                                     'issues.no_issues.hidden_issues.' + type,
116229                                     { count: hiddenIssues.length.toString() }
116230                                 ));
116231                             return;
116232                         }
116233                     }
116234                     selection.select('.box .details')
116235                         .text(_t('issues.no_issues.hidden_issues.none'));
116236                 }
116237
116238                 var messageType;
116239
116240                 if (opts.what === 'edited' && opts.where === 'visible') {
116241
116242                     messageType = 'edits_in_view';
116243
116244                     checkForHiddenIssues({
116245                         elsewhere: { what: 'edited', where: 'all' },
116246                         everything_else: { what: 'all', where: 'visible' },
116247                         disabled_rules: { what: 'edited', where: 'visible', includeDisabledRules: 'only' },
116248                         everything_else_elsewhere: { what: 'all', where: 'all' },
116249                         disabled_rules_elsewhere: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116250                         ignored_issues: { what: 'edited', where: 'visible', includeIgnored: 'only' },
116251                         ignored_issues_elsewhere: { what: 'edited', where: 'all', includeIgnored: 'only' }
116252                     });
116253
116254                 } else if (opts.what === 'edited' && opts.where === 'all') {
116255
116256                     messageType = 'edits';
116257
116258                     checkForHiddenIssues({
116259                         everything_else: { what: 'all', where: 'all' },
116260                         disabled_rules: { what: 'edited', where: 'all', includeDisabledRules: 'only' },
116261                         ignored_issues: { what: 'edited', where: 'all', includeIgnored: 'only' }
116262                     });
116263
116264                 } else if (opts.what === 'all' && opts.where === 'visible') {
116265
116266                     messageType = 'everything_in_view';
116267
116268                     checkForHiddenIssues({
116269                         elsewhere: { what: 'all', where: 'all' },
116270                         disabled_rules: { what: 'all', where: 'visible', includeDisabledRules: 'only' },
116271                         disabled_rules_elsewhere: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116272                         ignored_issues: { what: 'all', where: 'visible', includeIgnored: 'only' },
116273                         ignored_issues_elsewhere: { what: 'all', where: 'all', includeIgnored: 'only' }
116274                     });
116275                 } else if (opts.what === 'all' && opts.where === 'all') {
116276
116277                     messageType = 'everything';
116278
116279                     checkForHiddenIssues({
116280                         disabled_rules: { what: 'all', where: 'all', includeDisabledRules: 'only' },
116281                         ignored_issues: { what: 'all', where: 'all', includeIgnored: 'only' }
116282                     });
116283                 }
116284
116285                 if (opts.what === 'edited' && context.history().difference().summary().length === 0) {
116286                     messageType = 'no_edits';
116287                 }
116288
116289                 selection.select('.box .message')
116290                     .text(_t('issues.no_issues.message.' + messageType));
116291
116292             }
116293
116294             context.validator().on('validated.uiSectionValidationStatus', function() {
116295                 window.requestIdleCallback(section.reRender);
116296             });
116297
116298             context.map().on('move.uiSectionValidationStatus',
116299                 debounce(function() {
116300                     window.requestIdleCallback(section.reRender);
116301                 }, 1000)
116302             );
116303
116304             return section;
116305         }
116306
116307         function uiPaneIssues(context) {
116308
116309             var issuesPane = uiPane('issues', context)
116310                 .key(_t('issues.key'))
116311                 .title(_t('issues.title'))
116312                 .description(_t('issues.title'))
116313                 .iconName('iD-icon-alert')
116314                 .sections([
116315                     uiSectionValidationOptions(context),
116316                     uiSectionValidationStatus(context),
116317                     uiSectionValidationIssues('issues-errors', 'error', context),
116318                     uiSectionValidationIssues('issues-warnings', 'warning', context),
116319                     uiSectionValidationRules(context)
116320                 ]);
116321
116322             return issuesPane;
116323         }
116324
116325         function uiSettingsCustomData(context) {
116326             var dispatch$1 = dispatch('change');
116327
116328             function render(selection) {
116329                 var dataLayer = context.layers().layer('data');
116330
116331                 // keep separate copies of original and current settings
116332                 var _origSettings = {
116333                     fileList: (dataLayer && dataLayer.fileList()) || null,
116334                     url: corePreferences('settings-custom-data-url')
116335                 };
116336                 var _currSettings = {
116337                     fileList: (dataLayer && dataLayer.fileList()) || null,
116338                     url: corePreferences('settings-custom-data-url')
116339                 };
116340
116341                 // var example = 'https://{switch:a,b,c}.tile.openstreetmap.org/{zoom}/{x}/{y}.png';
116342                 var modal = uiConfirm(selection).okButton();
116343
116344                 modal
116345                     .classed('settings-modal settings-custom-data', true);
116346
116347                 modal.select('.modal-section.header')
116348                     .append('h3')
116349                     .text(_t('settings.custom_data.header'));
116350
116351
116352                 var textSection = modal.select('.modal-section.message-text');
116353
116354                 textSection
116355                     .append('pre')
116356                     .attr('class', 'instructions-file')
116357                     .text(_t('settings.custom_data.file.instructions'));
116358
116359                 textSection
116360                     .append('input')
116361                     .attr('class', 'field-file')
116362                     .attr('type', 'file')
116363                     .property('files', _currSettings.fileList)  // works for all except IE11
116364                     .on('change', function() {
116365                         var files = event.target.files;
116366                         if (files && files.length) {
116367                             _currSettings.url = '';
116368                             textSection.select('.field-url').property('value', '');
116369                             _currSettings.fileList = files;
116370                         } else {
116371                             _currSettings.fileList = null;
116372                         }
116373                     });
116374
116375                 textSection
116376                     .append('h4')
116377                     .text(_t('settings.custom_data.or'));
116378
116379                 textSection
116380                     .append('pre')
116381                     .attr('class', 'instructions-url')
116382                     .text(_t('settings.custom_data.url.instructions'));
116383
116384                 textSection
116385                     .append('textarea')
116386                     .attr('class', 'field-url')
116387                     .attr('placeholder', _t('settings.custom_data.url.placeholder'))
116388                     .call(utilNoAuto)
116389                     .property('value', _currSettings.url);
116390
116391
116392                 // insert a cancel button
116393                 var buttonSection = modal.select('.modal-section.buttons');
116394
116395                 buttonSection
116396                     .insert('button', '.ok-button')
116397                     .attr('class', 'button cancel-button secondary-action')
116398                     .text(_t('confirm.cancel'));
116399
116400
116401                 buttonSection.select('.cancel-button')
116402                     .on('click.cancel', clickCancel);
116403
116404                 buttonSection.select('.ok-button')
116405                     .attr('disabled', isSaveDisabled)
116406                     .on('click.save', clickSave);
116407
116408
116409                 function isSaveDisabled() {
116410                     return null;
116411                 }
116412
116413
116414                 // restore the original url
116415                 function clickCancel() {
116416                     textSection.select('.field-url').property('value', _origSettings.url);
116417                     corePreferences('settings-custom-data-url', _origSettings.url);
116418                     this.blur();
116419                     modal.close();
116420                 }
116421
116422                 // accept the current url
116423                 function clickSave() {
116424                     _currSettings.url = textSection.select('.field-url').property('value').trim();
116425
116426                     // one or the other but not both
116427                     if (_currSettings.url) { _currSettings.fileList = null; }
116428                     if (_currSettings.fileList) { _currSettings.url = ''; }
116429
116430                     corePreferences('settings-custom-data-url', _currSettings.url);
116431                     this.blur();
116432                     modal.close();
116433                     dispatch$1.call('change', this, _currSettings);
116434                 }
116435             }
116436
116437             return utilRebind(render, dispatch$1, 'on');
116438         }
116439
116440         function uiSectionDataLayers(context) {
116441
116442             var settingsCustomData = uiSettingsCustomData(context)
116443                 .on('change', customChanged);
116444
116445             var layers = context.layers();
116446
116447             var section = uiSection('data-layers', context)
116448                 .title(_t('map_data.data_layers'))
116449                 .disclosureContent(renderDisclosureContent);
116450
116451             function renderDisclosureContent(selection) {
116452                 var container = selection.selectAll('.data-layer-container')
116453                     .data([0]);
116454
116455                 container.enter()
116456                     .append('div')
116457                     .attr('class', 'data-layer-container')
116458                     .merge(container)
116459                     .call(drawOsmItems)
116460                     .call(drawQAItems)
116461                     .call(drawCustomDataItems)
116462                     .call(drawVectorItems)      // Beta - Detroit mapping challenge
116463                     .call(drawPanelItems);
116464             }
116465
116466             function showsLayer(which) {
116467                 var layer = layers.layer(which);
116468                 if (layer) {
116469                     return layer.enabled();
116470                 }
116471                 return false;
116472             }
116473
116474             function setLayer(which, enabled) {
116475                 // Don't allow layer changes while drawing - #6584
116476                 var mode = context.mode();
116477                 if (mode && /^draw/.test(mode.id)) { return; }
116478
116479                 var layer = layers.layer(which);
116480                 if (layer) {
116481                     layer.enabled(enabled);
116482
116483                     if (!enabled && (which === 'osm' || which === 'notes')) {
116484                         context.enter(modeBrowse(context));
116485                     }
116486                 }
116487             }
116488
116489             function toggleLayer(which) {
116490                 setLayer(which, !showsLayer(which));
116491             }
116492
116493             function drawOsmItems(selection) {
116494                 var osmKeys = ['osm', 'notes'];
116495                 var osmLayers = layers.all().filter(function(obj) { return osmKeys.indexOf(obj.id) !== -1; });
116496
116497                 var ul = selection
116498                     .selectAll('.layer-list-osm')
116499                     .data([0]);
116500
116501                 ul = ul.enter()
116502                     .append('ul')
116503                     .attr('class', 'layer-list layer-list-osm')
116504                     .merge(ul);
116505
116506                 var li = ul.selectAll('.list-item')
116507                     .data(osmLayers);
116508
116509                 li.exit()
116510                     .remove();
116511
116512                 var liEnter = li.enter()
116513                     .append('li')
116514                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116515
116516                 var labelEnter = liEnter
116517                     .append('label')
116518                     .each(function(d) {
116519                         if (d.id === 'osm') {
116520                             select(this)
116521                                 .call(uiTooltip()
116522                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116523                                     .keys([uiCmd('⌥' + _t('area_fill.wireframe.key'))])
116524                                     .placement('bottom')
116525                                 );
116526                         } else {
116527                             select(this)
116528                                 .call(uiTooltip()
116529                                     .title(_t('map_data.layers.' + d.id + '.tooltip'))
116530                                     .placement('bottom')
116531                                 );
116532                         }
116533                     });
116534
116535                 labelEnter
116536                     .append('input')
116537                     .attr('type', 'checkbox')
116538                     .on('change', function(d) { toggleLayer(d.id); });
116539
116540                 labelEnter
116541                     .append('span')
116542                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116543
116544
116545                 // Update
116546                 li
116547                     .merge(liEnter)
116548                     .classed('active', function (d) { return d.layer.enabled(); })
116549                     .selectAll('input')
116550                     .property('checked', function (d) { return d.layer.enabled(); });
116551             }
116552
116553             function drawQAItems(selection) {
116554                 var qaKeys = ['keepRight', 'improveOSM', 'osmose'];
116555                 var qaLayers = layers.all().filter(function(obj) { return qaKeys.indexOf(obj.id) !== -1; });
116556
116557                 var ul = selection
116558                     .selectAll('.layer-list-qa')
116559                     .data([0]);
116560
116561                 ul = ul.enter()
116562                     .append('ul')
116563                     .attr('class', 'layer-list layer-list-qa')
116564                     .merge(ul);
116565
116566                 var li = ul.selectAll('.list-item')
116567                     .data(qaLayers);
116568
116569                 li.exit()
116570                     .remove();
116571
116572                 var liEnter = li.enter()
116573                     .append('li')
116574                     .attr('class', function(d) { return 'list-item list-item-' + d.id; });
116575
116576                 var labelEnter = liEnter
116577                     .append('label')
116578                     .each(function(d) {
116579                         select(this)
116580                             .call(uiTooltip()
116581                                 .title(_t('map_data.layers.' + d.id + '.tooltip'))
116582                                 .placement('bottom')
116583                             );
116584                     });
116585
116586                 labelEnter
116587                     .append('input')
116588                     .attr('type', 'checkbox')
116589                     .on('change', function(d) { toggleLayer(d.id); });
116590
116591                 labelEnter
116592                     .append('span')
116593                     .text(function(d) { return _t('map_data.layers.' + d.id + '.title'); });
116594
116595
116596                 // Update
116597                 li
116598                     .merge(liEnter)
116599                     .classed('active', function (d) { return d.layer.enabled(); })
116600                     .selectAll('input')
116601                     .property('checked', function (d) { return d.layer.enabled(); });
116602             }
116603
116604             // Beta feature - sample vector layers to support Detroit Mapping Challenge
116605             // https://github.com/osmus/detroit-mapping-challenge
116606             function drawVectorItems(selection) {
116607                 var dataLayer = layers.layer('data');
116608                 var vtData = [
116609                     {
116610                         name: 'Detroit Neighborhoods/Parks',
116611                         src: 'neighborhoods-parks',
116612                         tooltip: 'Neighborhood boundaries and parks as compiled by City of Detroit in concert with community groups.',
116613                         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'
116614                     }, {
116615                         name: 'Detroit Composite POIs',
116616                         src: 'composite-poi',
116617                         tooltip: 'Fire Inspections, Business Licenses, and other public location data collated from the City of Detroit.',
116618                         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'
116619                     }, {
116620                         name: 'Detroit All-The-Places POIs',
116621                         src: 'alltheplaces-poi',
116622                         tooltip: 'Public domain business location data created by web scrapers.',
116623                         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'
116624                     }
116625                 ];
116626
116627                 // Only show this if the map is around Detroit..
116628                 var detroit = geoExtent([-83.5, 42.1], [-82.8, 42.5]);
116629                 var showVectorItems = (context.map().zoom() > 9 && detroit.contains(context.map().center()));
116630
116631                 var container = selection.selectAll('.vectortile-container')
116632                     .data(showVectorItems ? [0] : []);
116633
116634                 container.exit()
116635                     .remove();
116636
116637                 var containerEnter = container.enter()
116638                     .append('div')
116639                     .attr('class', 'vectortile-container');
116640
116641                 containerEnter
116642                     .append('h4')
116643                     .attr('class', 'vectortile-header')
116644                     .text('Detroit Vector Tiles (Beta)');
116645
116646                 containerEnter
116647                     .append('ul')
116648                     .attr('class', 'layer-list layer-list-vectortile');
116649
116650                 containerEnter
116651                     .append('div')
116652                     .attr('class', 'vectortile-footer')
116653                     .append('a')
116654                     .attr('target', '_blank')
116655                     .attr('tabindex', -1)
116656                     .call(svgIcon('#iD-icon-out-link', 'inline'))
116657                     .attr('href', 'https://github.com/osmus/detroit-mapping-challenge')
116658                     .append('span')
116659                     .text('About these layers');
116660
116661                 container = container
116662                     .merge(containerEnter);
116663
116664
116665                 var ul = container.selectAll('.layer-list-vectortile');
116666
116667                 var li = ul.selectAll('.list-item')
116668                     .data(vtData);
116669
116670                 li.exit()
116671                     .remove();
116672
116673                 var liEnter = li.enter()
116674                     .append('li')
116675                     .attr('class', function(d) { return 'list-item list-item-' + d.src; });
116676
116677                 var labelEnter = liEnter
116678                     .append('label')
116679                     .each(function(d) {
116680                         select(this).call(
116681                             uiTooltip().title(d.tooltip).placement('top')
116682                         );
116683                     });
116684
116685                 labelEnter
116686                     .append('input')
116687                     .attr('type', 'radio')
116688                     .attr('name', 'vectortile')
116689                     .on('change', selectVTLayer);
116690
116691                 labelEnter
116692                     .append('span')
116693                     .text(function(d) { return d.name; });
116694
116695                 // Update
116696                 li
116697                     .merge(liEnter)
116698                     .classed('active', isVTLayerSelected)
116699                     .selectAll('input')
116700                     .property('checked', isVTLayerSelected);
116701
116702
116703                 function isVTLayerSelected(d) {
116704                     return dataLayer && dataLayer.template() === d.template;
116705                 }
116706
116707                 function selectVTLayer(d) {
116708                     corePreferences('settings-custom-data-url', d.template);
116709                     if (dataLayer) {
116710                         dataLayer.template(d.template, d.src);
116711                         dataLayer.enabled(true);
116712                     }
116713                 }
116714             }
116715
116716             function drawCustomDataItems(selection) {
116717                 var dataLayer = layers.layer('data');
116718                 var hasData = dataLayer && dataLayer.hasData();
116719                 var showsData = hasData && dataLayer.enabled();
116720
116721                 var ul = selection
116722                     .selectAll('.layer-list-data')
116723                     .data(dataLayer ? [0] : []);
116724
116725                 // Exit
116726                 ul.exit()
116727                     .remove();
116728
116729                 // Enter
116730                 var ulEnter = ul.enter()
116731                     .append('ul')
116732                     .attr('class', 'layer-list layer-list-data');
116733
116734                 var liEnter = ulEnter
116735                     .append('li')
116736                     .attr('class', 'list-item-data');
116737
116738                 var labelEnter = liEnter
116739                     .append('label')
116740                     .call(uiTooltip()
116741                         .title(_t('map_data.layers.custom.tooltip'))
116742                         .placement('top')
116743                     );
116744
116745                 labelEnter
116746                     .append('input')
116747                     .attr('type', 'checkbox')
116748                     .on('change', function() { toggleLayer('data'); });
116749
116750                 labelEnter
116751                     .append('span')
116752                     .text(_t('map_data.layers.custom.title'));
116753
116754                 liEnter
116755                     .append('button')
116756                     .call(uiTooltip()
116757                         .title(_t('settings.custom_data.tooltip'))
116758                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116759                     )
116760                     .on('click', editCustom)
116761                     .call(svgIcon('#iD-icon-more'));
116762
116763                 liEnter
116764                     .append('button')
116765                     .call(uiTooltip()
116766                         .title(_t('map_data.layers.custom.zoom'))
116767                         .placement((_mainLocalizer.textDirection() === 'rtl') ? 'right' : 'left')
116768                     )
116769                     .on('click', function() {
116770                         event.preventDefault();
116771                         event.stopPropagation();
116772                         dataLayer.fitZoom();
116773                     })
116774                     .call(svgIcon('#iD-icon-framed-dot'));
116775
116776                 // Update
116777                 ul = ul
116778                     .merge(ulEnter);
116779
116780                 ul.selectAll('.list-item-data')
116781                     .classed('active', showsData)
116782                     .selectAll('label')
116783                     .classed('deemphasize', !hasData)
116784                     .selectAll('input')
116785                     .property('disabled', !hasData)
116786                     .property('checked', showsData);
116787             }
116788
116789             function editCustom() {
116790                 event.preventDefault();
116791                 context.container()
116792                     .call(settingsCustomData);
116793             }
116794
116795             function customChanged(d) {
116796                 var dataLayer = layers.layer('data');
116797
116798                 if (d && d.url) {
116799                     dataLayer.url(d.url);
116800                 } else if (d && d.fileList) {
116801                     dataLayer.fileList(d.fileList);
116802                 }
116803             }
116804
116805
116806             function drawPanelItems(selection) {
116807
116808                 var panelsListEnter = selection.selectAll('.md-extras-list')
116809                     .data([0])
116810                     .enter()
116811                     .append('ul')
116812                     .attr('class', 'layer-list md-extras-list');
116813
116814                 var historyPanelLabelEnter = panelsListEnter
116815                     .append('li')
116816                     .attr('class', 'history-panel-toggle-item')
116817                     .append('label')
116818                     .call(uiTooltip()
116819                         .title(_t('map_data.history_panel.tooltip'))
116820                         .keys([uiCmd('⌘⇧' + _t('info_panels.history.key'))])
116821                         .placement('top')
116822                     );
116823
116824                 historyPanelLabelEnter
116825                     .append('input')
116826                     .attr('type', 'checkbox')
116827                     .on('change', function() {
116828                         event.preventDefault();
116829                         context.ui().info.toggle('history');
116830                     });
116831
116832                 historyPanelLabelEnter
116833                     .append('span')
116834                     .text(_t('map_data.history_panel.title'));
116835
116836                 var measurementPanelLabelEnter = panelsListEnter
116837                     .append('li')
116838                     .attr('class', 'measurement-panel-toggle-item')
116839                     .append('label')
116840                     .call(uiTooltip()
116841                         .title(_t('map_data.measurement_panel.tooltip'))
116842                         .keys([uiCmd('⌘⇧' + _t('info_panels.measurement.key'))])
116843                         .placement('top')
116844                     );
116845
116846                 measurementPanelLabelEnter
116847                     .append('input')
116848                     .attr('type', 'checkbox')
116849                     .on('change', function() {
116850                         event.preventDefault();
116851                         context.ui().info.toggle('measurement');
116852                     });
116853
116854                 measurementPanelLabelEnter
116855                     .append('span')
116856                     .text(_t('map_data.measurement_panel.title'));
116857             }
116858
116859             context.layers().on('change.uiSectionDataLayers', section.reRender);
116860
116861             context.map()
116862                 .on('move.uiSectionDataLayers',
116863                     debounce(function() {
116864                         // Detroit layers may have moved in or out of view
116865                         window.requestIdleCallback(section.reRender);
116866                     }, 1000)
116867                 );
116868
116869             return section;
116870         }
116871
116872         function uiSectionMapFeatures(context) {
116873
116874             var _features = context.features().keys();
116875
116876             var section = uiSection('map-features', context)
116877                 .title(_t('map_data.map_features'))
116878                 .disclosureContent(renderDisclosureContent)
116879                 .expandedByDefault(false);
116880
116881             function renderDisclosureContent(selection) {
116882
116883                 var container = selection.selectAll('.layer-feature-list-container')
116884                     .data([0]);
116885
116886                 var containerEnter = container.enter()
116887                     .append('div')
116888                     .attr('class', 'layer-feature-list-container');
116889
116890                 containerEnter
116891                     .append('ul')
116892                     .attr('class', 'layer-list layer-feature-list');
116893
116894                 var footer = containerEnter
116895                     .append('div')
116896                     .attr('class', 'feature-list-links section-footer');
116897
116898                 footer
116899                     .append('a')
116900                     .attr('class', 'feature-list-link')
116901                     .attr('href', '#')
116902                     .text(_t('issues.enable_all'))
116903                     .on('click', function() {
116904                         context.features().enableAll();
116905                     });
116906
116907                 footer
116908                     .append('a')
116909                     .attr('class', 'feature-list-link')
116910                     .attr('href', '#')
116911                     .text(_t('issues.disable_all'))
116912                     .on('click', function() {
116913                         context.features().disableAll();
116914                     });
116915
116916                 // Update
116917                 container = container
116918                     .merge(containerEnter);
116919
116920                 container.selectAll('.layer-feature-list')
116921                     .call(drawListItems, _features, 'checkbox', 'feature', clickFeature, showsFeature);
116922             }
116923
116924             function drawListItems(selection, data, type, name, change, active) {
116925                 var items = selection.selectAll('li')
116926                     .data(data);
116927
116928                 // Exit
116929                 items.exit()
116930                     .remove();
116931
116932                 // Enter
116933                 var enter = items.enter()
116934                     .append('li')
116935                     .call(uiTooltip()
116936                         .title(function(d) {
116937                             var tip = _t(name + '.' + d + '.tooltip');
116938                             if (autoHiddenFeature(d)) {
116939                                 var msg = showsLayer('osm') ? _t('map_data.autohidden') : _t('map_data.osmhidden');
116940                                 tip += '<div>' + msg + '</div>';
116941                             }
116942                             return tip;
116943                         })
116944                         .placement('top')
116945                     );
116946
116947                 var label = enter
116948                     .append('label');
116949
116950                 label
116951                     .append('input')
116952                     .attr('type', type)
116953                     .attr('name', name)
116954                     .on('change', change);
116955
116956                 label
116957                     .append('span')
116958                     .text(function(d) { return _t(name + '.' + d + '.description'); });
116959
116960                 // Update
116961                 items = items
116962                     .merge(enter);
116963
116964                 items
116965                     .classed('active', active)
116966                     .selectAll('input')
116967                     .property('checked', active)
116968                     .property('indeterminate', autoHiddenFeature);
116969             }
116970
116971             function autoHiddenFeature(d) {
116972                 return context.features().autoHidden(d);
116973             }
116974
116975             function showsFeature(d) {
116976                 return context.features().enabled(d);
116977             }
116978
116979             function clickFeature(d) {
116980                 context.features().toggle(d);
116981             }
116982
116983             function showsLayer(id) {
116984                 var layer = context.layers().layer(id);
116985                 return layer && layer.enabled();
116986             }
116987
116988             // add listeners
116989             context.features()
116990                 .on('change.map_features', section.reRender);
116991
116992             return section;
116993         }
116994
116995         function uiSectionMapStyleOptions(context) {
116996
116997             var section = uiSection('fill-area', context)
116998                 .title(_t('map_data.style_options'))
116999                 .disclosureContent(renderDisclosureContent)
117000                 .expandedByDefault(false);
117001
117002             function renderDisclosureContent(selection) {
117003                 var container = selection.selectAll('.layer-fill-list')
117004                     .data([0]);
117005
117006                 container.enter()
117007                     .append('ul')
117008                     .attr('class', 'layer-list layer-fill-list')
117009                     .merge(container)
117010                     .call(drawListItems, context.map().areaFillOptions, 'radio', 'area_fill', setFill, isActiveFill);
117011
117012                 var container2 = selection.selectAll('.layer-visual-diff-list')
117013                     .data([0]);
117014
117015                 container2.enter()
117016                     .append('ul')
117017                     .attr('class', 'layer-list layer-visual-diff-list')
117018                     .merge(container2)
117019                     .call(drawListItems, ['highlight_edits'], 'checkbox', 'visual_diff', toggleHighlightEdited, function() {
117020                         return context.surface().classed('highlight-edited');
117021                     });
117022             }
117023
117024             function drawListItems(selection, data, type, name, change, active) {
117025                 var items = selection.selectAll('li')
117026                     .data(data);
117027
117028                 // Exit
117029                 items.exit()
117030                     .remove();
117031
117032                 // Enter
117033                 var enter = items.enter()
117034                     .append('li')
117035                     .call(uiTooltip()
117036                         .title(function(d) {
117037                             return _t(name + '.' + d + '.tooltip');
117038                         })
117039                         .keys(function(d) {
117040                             var key = (d === 'wireframe' ? _t('area_fill.wireframe.key') : null);
117041                             if (d === 'highlight_edits') { key = _t('map_data.highlight_edits.key'); }
117042                             return key ? [key] : null;
117043                         })
117044                         .placement('top')
117045                     );
117046
117047                 var label = enter
117048                     .append('label');
117049
117050                 label
117051                     .append('input')
117052                     .attr('type', type)
117053                     .attr('name', name)
117054                     .on('change', change);
117055
117056                 label
117057                     .append('span')
117058                     .text(function(d) { return _t(name + '.' + d + '.description'); });
117059
117060                 // Update
117061                 items = items
117062                     .merge(enter);
117063
117064                 items
117065                     .classed('active', active)
117066                     .selectAll('input')
117067                     .property('checked', active)
117068                     .property('indeterminate', false);
117069             }
117070
117071             function isActiveFill(d) {
117072                 return context.map().activeAreaFill() === d;
117073             }
117074
117075             function toggleHighlightEdited() {
117076                 event.preventDefault();
117077                 context.map().toggleHighlightEdited();
117078             }
117079
117080             function setFill(d) {
117081                 context.map().activeAreaFill(d);
117082             }
117083
117084             context.map()
117085                 .on('changeHighlighting.ui_style, changeAreaFill.ui_style', section.reRender);
117086
117087             return section;
117088         }
117089
117090         function uiSectionPhotoOverlays(context) {
117091
117092             var layers = context.layers();
117093
117094             var section = uiSection('photo-overlays', context)
117095                 .title(_t('photo_overlays.title'))
117096                 .disclosureContent(renderDisclosureContent)
117097                 .expandedByDefault(false);
117098
117099             function renderDisclosureContent(selection) {
117100                 var container = selection.selectAll('.photo-overlay-container')
117101                     .data([0]);
117102
117103                 container.enter()
117104                     .append('div')
117105                     .attr('class', 'photo-overlay-container')
117106                     .merge(container)
117107                     .call(drawPhotoItems)
117108                     .call(drawPhotoTypeItems);
117109             }
117110
117111             function drawPhotoItems(selection) {
117112                 var photoKeys = context.photos().overlayLayerIDs();
117113                 var photoLayers = layers.all().filter(function(obj) { return photoKeys.indexOf(obj.id) !== -1; });
117114                 var data = photoLayers.filter(function(obj) { return obj.layer.supported(); });
117115
117116                 function layerSupported(d) {
117117                     return d.layer && d.layer.supported();
117118                 }
117119                 function layerEnabled(d) {
117120                     return layerSupported(d) && d.layer.enabled();
117121                 }
117122
117123                 var ul = selection
117124                     .selectAll('.layer-list-photos')
117125                     .data([0]);
117126
117127                 ul = ul.enter()
117128                     .append('ul')
117129                     .attr('class', 'layer-list layer-list-photos')
117130                     .merge(ul);
117131
117132                 var li = ul.selectAll('.list-item-photos')
117133                     .data(data);
117134
117135                 li.exit()
117136                     .remove();
117137
117138                 var liEnter = li.enter()
117139                     .append('li')
117140                     .attr('class', function(d) {
117141                         var classes = 'list-item-photos list-item-' + d.id;
117142                         if (d.id === 'mapillary-signs' || d.id === 'mapillary-map-features') {
117143                             classes += ' indented';
117144                         }
117145                         return classes;
117146                     });
117147
117148                 var labelEnter = liEnter
117149                     .append('label')
117150                     .each(function(d) {
117151                         var titleID;
117152                         if (d.id === 'mapillary-signs') { titleID = 'mapillary.signs.tooltip'; }
117153                         else if (d.id === 'mapillary') { titleID = 'mapillary_images.tooltip'; }
117154                         else if (d.id === 'openstreetcam') { titleID = 'openstreetcam_images.tooltip'; }
117155                         else { titleID = d.id.replace(/-/g, '_') + '.tooltip'; }
117156                         select(this)
117157                             .call(uiTooltip()
117158                                 .title(_t(titleID))
117159                                 .placement('top')
117160                             );
117161                     });
117162
117163                 labelEnter
117164                     .append('input')
117165                     .attr('type', 'checkbox')
117166                     .on('change', function(d) { toggleLayer(d.id); });
117167
117168                 labelEnter
117169                     .append('span')
117170                     .text(function(d) {
117171                         var id = d.id;
117172                         if (id === 'mapillary-signs') { id = 'photo_overlays.traffic_signs'; }
117173                         return _t(id.replace(/-/g, '_') + '.title');
117174                     });
117175
117176
117177                 // Update
117178                 li
117179                     .merge(liEnter)
117180                     .classed('active', layerEnabled)
117181                     .selectAll('input')
117182                     .property('checked', layerEnabled);
117183             }
117184
117185             function drawPhotoTypeItems(selection) {
117186                 var data = context.photos().allPhotoTypes();
117187
117188                 function typeEnabled(d) {
117189                     return context.photos().showsPhotoType(d);
117190                 }
117191
117192                 var ul = selection
117193                     .selectAll('.layer-list-photo-types')
117194                     .data(context.photos().shouldFilterByPhotoType() ? [0] : []);
117195
117196                 ul.exit()
117197                     .remove();
117198
117199                 ul = ul.enter()
117200                     .append('ul')
117201                     .attr('class', 'layer-list layer-list-photo-types')
117202                     .merge(ul);
117203
117204                 var li = ul.selectAll('.list-item-photo-types')
117205                     .data(data);
117206
117207                 li.exit()
117208                     .remove();
117209
117210                 var liEnter = li.enter()
117211                     .append('li')
117212                     .attr('class', function(d) {
117213                         return 'list-item-photo-types list-item-' + d;
117214                     });
117215
117216                 var labelEnter = liEnter
117217                     .append('label')
117218                     .each(function(d) {
117219                         select(this)
117220                             .call(uiTooltip()
117221                                 .title(_t('photo_overlays.photo_type.' + d + '.tooltip'))
117222                                 .placement('top')
117223                             );
117224                     });
117225
117226                 labelEnter
117227                     .append('input')
117228                     .attr('type', 'checkbox')
117229                     .on('change', function(d) {
117230                         context.photos().togglePhotoType(d);
117231                     });
117232
117233                 labelEnter
117234                     .append('span')
117235                     .text(function(d) {
117236                         return _t('photo_overlays.photo_type.' + d + '.title');
117237                     });
117238
117239
117240                 // Update
117241                 li
117242                     .merge(liEnter)
117243                     .classed('active', typeEnabled)
117244                     .selectAll('input')
117245                     .property('checked', typeEnabled);
117246             }
117247
117248             function toggleLayer(which) {
117249                 setLayer(which, !showsLayer(which));
117250             }
117251
117252             function showsLayer(which) {
117253                 var layer = layers.layer(which);
117254                 if (layer) {
117255                     return layer.enabled();
117256                 }
117257                 return false;
117258             }
117259
117260             function setLayer(which, enabled) {
117261                 var layer = layers.layer(which);
117262                 if (layer) {
117263                     layer.enabled(enabled);
117264                 }
117265             }
117266
117267             context.layers().on('change.uiSectionPhotoOverlays', section.reRender);
117268             context.photos().on('change.uiSectionPhotoOverlays', section.reRender);
117269
117270             return section;
117271         }
117272
117273         function uiPaneMapData(context) {
117274
117275             var mapDataPane = uiPane('map-data', context)
117276                 .key(_t('map_data.key'))
117277                 .title(_t('map_data.title'))
117278                 .description(_t('map_data.description'))
117279                 .iconName('iD-icon-data')
117280                 .sections([
117281                     uiSectionDataLayers(context),
117282                     uiSectionPhotoOverlays(context),
117283                     uiSectionMapStyleOptions(context),
117284                     uiSectionMapFeatures(context)
117285                 ]);
117286
117287             return mapDataPane;
117288         }
117289
117290         function uiSectionPrivacy(context) {
117291
117292             var section = uiSection('preferences-third-party', context)
117293               .title(_t('preferences.privacy.title'))
117294               .disclosureContent(renderDisclosureContent);
117295
117296             var _showThirdPartyIcons = corePreferences('preferences.privacy.thirdpartyicons') || 'true';
117297
117298             function renderDisclosureContent(selection) {
117299               // enter
117300               var privacyOptionsListEnter = selection.selectAll('.privacy-options-list')
117301                 .data([0])
117302                 .enter()
117303                 .append('ul')
117304                 .attr('class', 'layer-list privacy-options-list');
117305
117306               var thirdPartyIconsEnter = privacyOptionsListEnter
117307                 .append('li')
117308                 .attr('class', 'privacy-third-party-icons-item')
117309                 .append('label')
117310                 .call(uiTooltip()
117311                   .title(_t('preferences.privacy.third_party_icons.tooltip'))
117312                   .placement('bottom')
117313                 );
117314
117315               thirdPartyIconsEnter
117316                 .append('input')
117317                 .attr('type', 'checkbox')
117318                 .on('change', function () {
117319                   event.preventDefault();
117320                   _showThirdPartyIcons = (_showThirdPartyIcons === 'true') ? 'false' : 'true';
117321                   corePreferences('preferences.privacy.thirdpartyicons', _showThirdPartyIcons);
117322                   update();
117323                 });
117324
117325               thirdPartyIconsEnter
117326                 .append('span')
117327                 .text(_t('preferences.privacy.third_party_icons.description'));
117328
117329
117330               // Privacy Policy link
117331               selection.selectAll('.privacy-link')
117332                 .data([0])
117333                 .enter()
117334                 .append('div')
117335                 .attr('class', 'privacy-link')
117336                 .append('a')
117337                 .attr('target', '_blank')
117338                 .call(svgIcon('#iD-icon-out-link', 'inline'))
117339                 .attr('href', 'https://github.com/openstreetmap/iD/blob/release/PRIVACY.md')
117340                 .append('span')
117341                 .text(_t('preferences.privacy.privacy_link'));
117342
117343               update();
117344
117345
117346               function update() {
117347                 selection.selectAll('.privacy-third-party-icons-item')
117348                   .classed('active', (_showThirdPartyIcons === 'true'))
117349                   .select('input')
117350                   .property('checked', (_showThirdPartyIcons === 'true'));
117351               }
117352             }
117353
117354             return section;
117355         }
117356
117357         function uiPanePreferences(context) {
117358
117359           var preferencesPane = uiPane('preferences', context)
117360             .key(_t('preferences.key'))
117361             .title(_t('preferences.title'))
117362             .description(_t('preferences.description'))
117363             .iconName('fas-user-cog')
117364             .sections([
117365                 uiSectionPrivacy(context)
117366             ]);
117367
117368           return preferencesPane;
117369         }
117370
117371         function uiInit(context) {
117372             var _initCounter = 0;
117373             var _needWidth = {};
117374
117375             var _lastPointerType;
117376
117377
117378             function render(container) {
117379
117380                 container
117381                     .on('click.ui', function() {
117382                         // we're only concerned with the primary mouse button
117383                         if (event.button !== 0) { return; }
117384
117385                         if (!event.composedPath) { return; }
117386
117387                         // some targets have default click events we don't want to override
117388                         var isOkayTarget = event.composedPath().some(function(node) {
117389                             // clicking <label> affects its <input> by default
117390                             return node.nodeName === 'LABEL' ||
117391                                 // clicking <a> opens a hyperlink by default
117392                                 node.nodeName === 'A';
117393                         });
117394                         if (isOkayTarget) { return; }
117395
117396                         // disable double-tap-to-zoom on touchscreens
117397                         event.preventDefault();
117398                     });
117399
117400                 var detected = utilDetect();
117401
117402                 // only WebKit supports gesture events
117403                 if ('GestureEvent' in window &&
117404                     // Listening for gesture events on iOS 13.4+ breaks double-tapping,
117405                     // but we only need to do this on desktop Safari anyway. – #7694
117406                     !detected.isMobileWebKit) {
117407
117408                     // On iOS we disable pinch-to-zoom of the UI via the `touch-action`
117409                     // CSS property, but on desktop Safari we need to manually cancel the
117410                     // default gesture events.
117411                     container.on('gesturestart.ui gesturechange.ui gestureend.ui', function() {
117412                         // disable pinch-to-zoom of the UI via multitouch trackpads on macOS Safari
117413                         event.preventDefault();
117414                     });
117415                 }
117416
117417                 if ('PointerEvent' in window) {
117418                     select(window)
117419                         .on('pointerdown.ui pointerup.ui', function() {
117420                             var pointerType = event.pointerType || 'mouse';
117421                             if (_lastPointerType !== pointerType) {
117422                                 _lastPointerType = pointerType;
117423                                 container
117424                                     .attr('pointer', pointerType);
117425                             }
117426                         }, true);
117427                 } else {
117428                     _lastPointerType = 'mouse';
117429                     container
117430                         .attr('pointer', 'mouse');
117431                 }
117432
117433                 container
117434                     .attr('dir', _mainLocalizer.textDirection());
117435
117436                 // setup fullscreen keybindings (no button shown at this time)
117437                 container
117438                     .call(uiFullScreen(context));
117439
117440                 var map = context.map();
117441                 map.redrawEnable(false);  // don't draw until we've set zoom/lat/long
117442
117443                 map
117444                     .on('hitMinZoom.ui', function() {
117445                         ui.flash.text(_t('cannot_zoom'))();
117446                     });
117447
117448                 container
117449                     .append('svg')
117450                     .attr('id', 'ideditor-defs')
117451                     .call(svgDefs(context));
117452
117453                 container
117454                     .append('div')
117455                     .attr('class', 'sidebar')
117456                     .call(ui.sidebar);
117457
117458                 var content = container
117459                     .append('div')
117460                     .attr('class', 'main-content active');
117461
117462                 // Top toolbar
117463                 content
117464                     .append('div')
117465                     .attr('class', 'top-toolbar-wrap')
117466                     .append('div')
117467                     .attr('class', 'top-toolbar fillD')
117468                     .call(uiTopToolbar(context));
117469
117470                 content
117471                     .append('div')
117472                     .attr('class', 'main-map')
117473                     .attr('dir', 'ltr')
117474                     .call(map);
117475
117476                 content
117477                     .append('div')
117478                     .attr('class', 'spinner')
117479                     .call(uiSpinner(context));
117480
117481                 // Add attribution and footer
117482                 var about = content
117483                     .append('div')
117484                     .attr('class', 'map-footer');
117485
117486                 about
117487                     .append('div')
117488                     .attr('class', 'attribution-wrap')
117489                     .attr('dir', 'ltr')
117490                     .call(uiAttribution(context));
117491
117492                 about
117493                     .append('div')
117494                     .attr('class', 'api-status')
117495                     .call(uiStatus(context));
117496
117497
117498                 var footer = about
117499                     .append('div')
117500                     .attr('class', 'map-footer-bar fillD');
117501
117502                 footer
117503                     .append('div')
117504                     .attr('class', 'flash-wrap footer-hide');
117505
117506                 var footerWrap = footer
117507                     .append('div')
117508                     .attr('class', 'main-footer-wrap footer-show');
117509
117510                 footerWrap
117511                     .append('div')
117512                     .attr('class', 'scale-block')
117513                     .call(uiScale(context));
117514
117515                 var aboutList = footerWrap
117516                     .append('div')
117517                     .attr('class', 'info-block')
117518                     .append('ul')
117519                     .attr('class', 'map-footer-list');
117520
117521                 if (!context.embed()) {
117522                     aboutList
117523                         .call(uiAccount(context));
117524                 }
117525
117526                 aboutList
117527                     .append('li')
117528                     .attr('class', 'version')
117529                     .call(uiVersion(context));
117530
117531                 var issueLinks = aboutList
117532                     .append('li');
117533
117534                 issueLinks
117535                     .append('a')
117536                     .attr('target', '_blank')
117537                     .attr('href', 'https://github.com/openstreetmap/iD/issues')
117538                     .call(svgIcon('#iD-icon-bug', 'light'))
117539                     .call(uiTooltip().title(_t('report_a_bug')).placement('top'));
117540
117541                 issueLinks
117542                     .append('a')
117543                     .attr('target', '_blank')
117544                     .attr('href', 'https://github.com/openstreetmap/iD/blob/develop/CONTRIBUTING.md#translating')
117545                     .call(svgIcon('#iD-icon-translate', 'light'))
117546                     .call(uiTooltip().title(_t('help_translate')).placement('top'));
117547
117548                 aboutList
117549                     .append('li')
117550                     .attr('class', 'feature-warning')
117551                     .attr('tabindex', -1)
117552                     .call(uiFeatureInfo(context));
117553
117554                 aboutList
117555                     .append('li')
117556                     .attr('class', 'issues-info')
117557                     .attr('tabindex', -1)
117558                     .call(uiIssuesInfo(context));
117559
117560                 var apiConnections = context.apiConnections();
117561                 if (apiConnections && apiConnections.length > 1) {
117562                     aboutList
117563                         .append('li')
117564                         .attr('class', 'source-switch')
117565                         .attr('tabindex', -1)
117566                         .call(uiSourceSwitch(context)
117567                             .keys(apiConnections)
117568                         );
117569                 }
117570
117571                 aboutList
117572                     .append('li')
117573                     .attr('class', 'user-list')
117574                     .attr('tabindex', -1)
117575                     .call(uiContributors(context));
117576
117577
117578                 // Setup map dimensions and move map to initial center/zoom.
117579                 // This should happen after .main-content and toolbars exist.
117580                 ui.onResize();
117581                 map.redrawEnable(true);
117582
117583                 ui.hash = behaviorHash(context);
117584                 ui.hash();
117585                 if (!ui.hash.hadHash) {
117586                     map.centerZoom([0, 0], 2);
117587                 }
117588
117589
117590                 var overMap = content
117591                     .append('div')
117592                     .attr('class', 'over-map');
117593
117594                 // Map controls
117595                 var controls = overMap
117596                     .append('div')
117597                     .attr('class', 'map-controls');
117598
117599                 controls
117600                     .append('div')
117601                     .attr('class', 'map-control zoombuttons')
117602                     .call(uiZoom(context));
117603
117604                 controls
117605                     .append('div')
117606                     .attr('class', 'map-control zoom-to-selection-control')
117607                     .call(uiZoomToSelection(context));
117608
117609                 controls
117610                     .append('div')
117611                     .attr('class', 'map-control geolocate-control')
117612                     .call(uiGeolocate(context));
117613
117614                 // Add panes
117615                 // This should happen after map is initialized, as some require surface()
117616                 var panes = overMap
117617                     .append('div')
117618                     .attr('class', 'map-panes');
117619
117620                 var uiPanes = [
117621                     uiPaneBackground(context),
117622                     uiPaneMapData(context),
117623                     uiPaneIssues(context),
117624                     uiPanePreferences(context),
117625                     uiPaneHelp(context)
117626                 ];
117627
117628                 uiPanes.forEach(function(pane) {
117629                     controls
117630                         .append('div')
117631                         .attr('class', 'map-control map-pane-control ' + pane.id + '-control')
117632                         .call(pane.renderToggleButton);
117633
117634                     panes
117635                         .call(pane.renderPane);
117636                 });
117637
117638                 ui.info = uiInfo(context);
117639
117640                 // Add absolutely-positioned elements that sit on top of the map
117641                 // This should happen after the map is ready (center/zoom)
117642                 overMap
117643                     .call(uiMapInMap(context))
117644                     .call(ui.info)
117645                     .call(uiNotice(context));
117646
117647
117648                 overMap
117649                     .append('div')
117650                     .attr('class', 'photoviewer')
117651                     .classed('al', true)       // 'al'=left,  'ar'=right
117652                     .classed('hide', true)
117653                     .call(ui.photoviewer);
117654
117655
117656                 // Bind events
117657                 window.onbeforeunload = function() {
117658                     return context.save();
117659                 };
117660                 window.onunload = function() {
117661                     context.history().unlock();
117662                 };
117663
117664                 select(window)
117665                     .on('resize.editor', ui.onResize);
117666
117667
117668                 var panPixels = 80;
117669                 context.keybinding()
117670                     .on('⌫', function() { event.preventDefault(); })
117671                     .on([_t('sidebar.key'), '`', '²', '@'], ui.sidebar.toggle)   // #5663, #6864 - common QWERTY, AZERTY
117672                     .on('←', pan([panPixels, 0]))
117673                     .on('↑', pan([0, panPixels]))
117674                     .on('→', pan([-panPixels, 0]))
117675                     .on('↓', pan([0, -panPixels]))
117676                     .on(uiCmd('⌘←'), pan([map.dimensions()[0], 0]))
117677                     .on(uiCmd('⌘↑'), pan([0, map.dimensions()[1]]))
117678                     .on(uiCmd('⌘→'), pan([-map.dimensions()[0], 0]))
117679                     .on(uiCmd('⌘↓'), pan([0, -map.dimensions()[1]]))
117680                     .on(uiCmd('⌘' + _t('background.key')), function quickSwitch() {
117681                         if (event) {
117682                             event.stopImmediatePropagation();
117683                             event.preventDefault();
117684                         }
117685                         var previousBackground = context.background().findSource(corePreferences('background-last-used-toggle'));
117686                         if (previousBackground) {
117687                             var currentBackground = context.background().baseLayerSource();
117688                             corePreferences('background-last-used-toggle', currentBackground.id);
117689                             corePreferences('background-last-used', previousBackground.id);
117690                             context.background().baseLayerSource(previousBackground);
117691                         }
117692                     })
117693                     .on(_t('area_fill.wireframe.key'), function toggleWireframe() {
117694                         event.preventDefault();
117695                         event.stopPropagation();
117696                         context.map().toggleWireframe();
117697                     })
117698                     .on(uiCmd('⌥' + _t('area_fill.wireframe.key')), function toggleOsmData() {
117699                         event.preventDefault();
117700                         event.stopPropagation();
117701
117702                         // Don't allow layer changes while drawing - #6584
117703                         var mode = context.mode();
117704                         if (mode && /^draw/.test(mode.id)) { return; }
117705
117706                         var layer = context.layers().layer('osm');
117707                         if (layer) {
117708                             layer.enabled(!layer.enabled());
117709                             if (!layer.enabled()) {
117710                                 context.enter(modeBrowse(context));
117711                             }
117712                         }
117713                     })
117714                     .on(_t('map_data.highlight_edits.key'), function toggleHighlightEdited() {
117715                         event.preventDefault();
117716                         context.map().toggleHighlightEdited();
117717                     });
117718
117719                 context
117720                     .on('enter.editor', function(entered) {
117721                         container
117722                             .classed('mode-' + entered.id, true);
117723                     })
117724                     .on('exit.editor', function(exited) {
117725                         container
117726                             .classed('mode-' + exited.id, false);
117727                     });
117728
117729                 context.enter(modeBrowse(context));
117730
117731                 if (!_initCounter++) {
117732                     if (!ui.hash.startWalkthrough) {
117733                         context.container()
117734                             .call(uiSplash(context))
117735                             .call(uiRestore(context));
117736                     }
117737
117738                     context.container()
117739                         .call(uiShortcuts(context));
117740                 }
117741
117742                 var osm = context.connection();
117743                 var auth = uiLoading(context).message(_t('loading_auth')).blocking(true);
117744
117745                 if (osm && auth) {
117746                     osm
117747                         .on('authLoading.ui', function() {
117748                             context.container()
117749                                 .call(auth);
117750                         })
117751                         .on('authDone.ui', function() {
117752                             auth.close();
117753                         });
117754                 }
117755
117756                 _initCounter++;
117757
117758                 if (ui.hash.startWalkthrough) {
117759                     ui.hash.startWalkthrough = false;
117760                     context.container().call(uiIntro(context));
117761                 }
117762
117763
117764                 function pan(d) {
117765                     return function() {
117766                         if (event.shiftKey) { return; }
117767                         if (context.container().select('.combobox').size()) { return; }
117768                         event.preventDefault();
117769                         context.map().pan(d, 100);
117770                     };
117771                 }
117772
117773             }
117774
117775
117776             var ui = {};
117777
117778             var _loadPromise;
117779             // renders the iD interface into the container node
117780             ui.ensureLoaded = function () {
117781
117782                 if (_loadPromise) { return _loadPromise; }
117783
117784                 return _loadPromise = Promise.all([
117785                         // must have strings and presets before loading the UI
117786                         _mainLocalizer.ensureLoaded(),
117787                         _mainPresetIndex.ensureLoaded()
117788                     ])
117789                     .then(function () {
117790                         if (!context.container().empty()) { render(context.container()); }
117791                     })
117792                     .catch(function (err) { return console.error(err); });  // eslint-disable-line
117793             };
117794
117795
117796             // `ui.restart()` will destroy and rebuild the entire iD interface,
117797             // for example to switch the locale while iD is running.
117798             ui.restart = function() {
117799                 context.keybinding().clear();
117800
117801                 _loadPromise = null;
117802
117803                 context.container().selectAll('*').remove();
117804
117805                 ui.ensureLoaded();
117806             };
117807
117808             ui.lastPointerType = function() {
117809                 return _lastPointerType;
117810             };
117811
117812             ui.flash = uiFlash(context);
117813
117814             ui.sidebar = uiSidebar(context);
117815
117816             ui.photoviewer = uiPhotoviewer(context);
117817
117818             ui.onResize = function(withPan) {
117819                 var map = context.map();
117820
117821                 // Recalc dimensions of map and sidebar.. (`true` = force recalc)
117822                 // This will call `getBoundingClientRect` and trigger reflow,
117823                 //  but the values will be cached for later use.
117824                 var mapDimensions = utilGetDimensions(context.container().select('.main-content'), true);
117825                 utilGetDimensions(context.container().select('.sidebar'), true);
117826
117827                 if (withPan !== undefined) {
117828                     map.redrawEnable(false);
117829                     map.pan(withPan);
117830                     map.redrawEnable(true);
117831                 }
117832                 map.dimensions(mapDimensions);
117833
117834                 ui.photoviewer.onMapResize();
117835
117836                 // check if header or footer have overflowed
117837                 ui.checkOverflow('.top-toolbar');
117838                 ui.checkOverflow('.map-footer-bar');
117839
117840                 // Use outdated code so it works on Explorer
117841                 var resizeWindowEvent = document.createEvent('Event');
117842
117843                 resizeWindowEvent.initEvent('resizeWindow', true, true);
117844
117845                 document.dispatchEvent(resizeWindowEvent);
117846             };
117847
117848
117849             // Call checkOverflow when resizing or whenever the contents change.
117850             ui.checkOverflow = function(selector, reset) {
117851                 if (reset) {
117852                     delete _needWidth[selector];
117853                 }
117854
117855                 var element = select(selector);
117856                 var scrollWidth = element.property('scrollWidth');
117857                 var clientWidth = element.property('clientWidth');
117858                 var needed = _needWidth[selector] || scrollWidth;
117859
117860                 if (scrollWidth > clientWidth) {    // overflow happening
117861                     element.classed('narrow', true);
117862                     if (!_needWidth[selector]) {
117863                         _needWidth[selector] = scrollWidth;
117864                     }
117865
117866                 } else if (scrollWidth >= needed) {
117867                     element.classed('narrow', false);
117868                 }
117869             };
117870
117871             ui.togglePanes = function(showPane) {
117872                 var shownPanes = context.container().selectAll('.map-pane.shown');
117873
117874                 var side = _mainLocalizer.textDirection() === 'ltr' ? 'right' : 'left';
117875
117876                 shownPanes
117877                     .classed('shown', false);
117878
117879                 context.container().selectAll('.map-pane-control button')
117880                     .classed('active', false);
117881
117882                 if (showPane) {
117883                     shownPanes
117884                         .style('display', 'none')
117885                         .style(side, '-500px');
117886
117887                     context.container().selectAll('.' + showPane.attr('pane') + '-control button')
117888                         .classed('active', true);
117889
117890                     showPane
117891                         .classed('shown', true)
117892                         .style('display', 'block');
117893                     if (shownPanes.empty()) {
117894                         showPane
117895                             .style('display', 'block')
117896                             .style(side, '-500px')
117897                             .transition()
117898                             .duration(200)
117899                             .style(side, '0px');
117900                     } else {
117901                         showPane
117902                             .style(side, '0px');
117903                     }
117904                 } else {
117905                     shownPanes
117906                         .style('display', 'block')
117907                         .style(side, '0px')
117908                         .transition()
117909                         .duration(200)
117910                         .style(side, '-500px')
117911                         .on('end', function() {
117912                             select(this).style('display', 'none');
117913                         });
117914                 }
117915             };
117916
117917
117918             var _editMenu = uiEditMenu(context);
117919
117920             ui.editMenu = function() {
117921                 return _editMenu;
117922             };
117923
117924             ui.showEditMenu = function(anchorPoint, triggerType, operations) {
117925
117926                 // remove any displayed menu
117927                 ui.closeEditMenu();
117928
117929                 if (!operations && context.mode().operations) { operations = context.mode().operations(); }
117930                 if (!operations || !operations.length) { return; }
117931
117932                 // disable menu if in wide selection, for example
117933                 if (!context.map().editableDataEnabled()) { return; }
117934
117935                 var surfaceNode = context.surface().node();
117936                 if (surfaceNode.focus) {   // FF doesn't support it
117937                     // focus the surface or else clicking off the menu may not trigger modeBrowse
117938                     surfaceNode.focus();
117939                 }
117940
117941                 operations.forEach(function(operation) {
117942                     if (operation.point) { operation.point(anchorPoint); }
117943                 });
117944
117945                 _editMenu
117946                     .anchorLoc(anchorPoint)
117947                     .triggerType(triggerType)
117948                     .operations(operations);
117949
117950                 // render the menu
117951                 context.map().supersurface.call(_editMenu);
117952             };
117953
117954             ui.closeEditMenu = function() {
117955                 // remove any existing menu no matter how it was added
117956                 context.map().supersurface
117957                     .select('.edit-menu').remove();
117958             };
117959
117960
117961             var _saveLoading = select(null);
117962
117963             context.uploader()
117964                 .on('saveStarted.ui', function() {
117965                     _saveLoading = uiLoading(context)
117966                         .message(_t('save.uploading'))
117967                         .blocking(true);
117968                     context.container().call(_saveLoading);  // block input during upload
117969                 })
117970                 .on('saveEnded.ui', function() {
117971                     _saveLoading.close();
117972                     _saveLoading = select(null);
117973                 });
117974
117975             return ui;
117976         }
117977
117978         function coreContext() {
117979           var this$1 = this;
117980
117981           var dispatch$1 = dispatch('enter', 'exit', 'change');
117982           var context = utilRebind({}, dispatch$1, 'on');
117983           var _deferred = new Set();
117984
117985           context.version = '2.18.2';
117986           context.privacyVersion = '20200407';
117987
117988           // iD will alter the hash so cache the parameters intended to setup the session
117989           context.initialHashParams = window.location.hash ? utilStringQs(window.location.hash) : {};
117990
117991           context.isFirstSession = !corePreferences('sawSplash') && !corePreferences('sawPrivacyVersion');
117992
117993
117994           /* Changeset */
117995           // An osmChangeset object. Not loaded until needed.
117996           context.changeset = null;
117997
117998           var _defaultChangesetComment = context.initialHashParams.comment;
117999           var _defaultChangesetSource = context.initialHashParams.source;
118000           var _defaultChangesetHashtags = context.initialHashParams.hashtags;
118001           context.defaultChangesetComment = function(val) {
118002             if (!arguments.length) { return _defaultChangesetComment; }
118003             _defaultChangesetComment = val;
118004             return context;
118005           };
118006           context.defaultChangesetSource = function(val) {
118007             if (!arguments.length) { return _defaultChangesetSource; }
118008             _defaultChangesetSource = val;
118009             return context;
118010           };
118011           context.defaultChangesetHashtags = function(val) {
118012             if (!arguments.length) { return _defaultChangesetHashtags; }
118013             _defaultChangesetHashtags = val;
118014             return context;
118015           };
118016
118017           /* Document title */
118018           /* (typically shown as the label for the browser window/tab) */
118019
118020           // If true, iD will update the title based on what the user is doing
118021           var _setsDocumentTitle = true;
118022           context.setsDocumentTitle = function(val) {
118023             if (!arguments.length) { return _setsDocumentTitle; }
118024             _setsDocumentTitle = val;
118025             return context;
118026           };
118027           // The part of the title that is always the same
118028           var _documentTitleBase = document.title;
118029           context.documentTitleBase = function(val) {
118030             if (!arguments.length) { return _documentTitleBase; }
118031             _documentTitleBase = val;
118032             return context;
118033           };
118034
118035
118036           /* User interface and keybinding */
118037           var _ui;
118038           context.ui = function () { return _ui; };
118039           context.lastPointerType = function () { return _ui.lastPointerType(); };
118040
118041           var _keybinding = utilKeybinding('context');
118042           context.keybinding = function () { return _keybinding; };
118043           select(document).call(_keybinding);
118044
118045
118046           /* Straight accessors. Avoid using these if you can. */
118047           // Instantiate the connection here because it doesn't require passing in
118048           // `context` and it's needed for pre-init calls like `preauth`
118049           var _connection = services.osm;
118050           var _history;
118051           var _validator;
118052           var _uploader;
118053           context.connection = function () { return _connection; };
118054           context.history = function () { return _history; };
118055           context.validator = function () { return _validator; };
118056           context.uploader = function () { return _uploader; };
118057
118058           /* Connection */
118059           context.preauth = function (options) {
118060             if (_connection) {
118061               _connection.switch(options);
118062             }
118063             return context;
118064           };
118065
118066           /* connection options for source switcher (optional) */
118067           var _apiConnections;
118068           context.apiConnections = function(val) {
118069             if (!arguments.length) { return _apiConnections; }
118070             _apiConnections = val;
118071             return context;
118072           };
118073
118074
118075           // A string or array or locale codes to prefer over the browser's settings
118076           context.locale = function(locale) {
118077             if (!arguments.length) { return _mainLocalizer.localeCode(); }
118078             _mainLocalizer.preferredLocaleCodes(locale);
118079             return context;
118080           };
118081
118082
118083           function afterLoad(cid, callback) {
118084             return function (err, result) {
118085               if (err) {
118086                 // 400 Bad Request, 401 Unauthorized, 403 Forbidden..
118087                 if (err.status === 400 || err.status === 401 || err.status === 403) {
118088                   if (_connection) {
118089                     _connection.logout();
118090                   }
118091                 }
118092                 if (typeof callback === 'function') {
118093                   callback(err);
118094                 }
118095                 return;
118096
118097               } else if (_connection && _connection.getConnectionId() !== cid) {
118098                 if (typeof callback === 'function') {
118099                   callback({ message: 'Connection Switched', status: -1 });
118100                 }
118101                 return;
118102
118103               } else {
118104                 _history.merge(result.data, result.extent);
118105                 if (typeof callback === 'function') {
118106                   callback(err, result);
118107                 }
118108                 return;
118109               }
118110             };
118111           }
118112
118113
118114           context.loadTiles = function (projection, callback) {
118115             var handle = window.requestIdleCallback(function () {
118116               _deferred.delete(handle);
118117               if (_connection && context.editableDataEnabled()) {
118118                 var cid = _connection.getConnectionId();
118119                 _connection.loadTiles(projection, afterLoad(cid, callback));
118120               }
118121             });
118122             _deferred.add(handle);
118123           };
118124
118125           context.loadTileAtLoc = function (loc, callback) {
118126             var handle = window.requestIdleCallback(function () {
118127               _deferred.delete(handle);
118128               if (_connection && context.editableDataEnabled()) {
118129                 var cid = _connection.getConnectionId();
118130                 _connection.loadTileAtLoc(loc, afterLoad(cid, callback));
118131               }
118132             });
118133             _deferred.add(handle);
118134           };
118135
118136           context.loadEntity = function (entityID, callback) {
118137             if (_connection) {
118138               var cid = _connection.getConnectionId();
118139               _connection.loadEntity(entityID, afterLoad(cid, callback));
118140             }
118141           };
118142
118143           context.zoomToEntity = function (entityID, zoomTo) {
118144             if (zoomTo !== false) {
118145               context.loadEntity(entityID, function (err, result) {
118146                 if (err) { return; }
118147                 var entity = result.data.find(function (e) { return e.id === entityID; });
118148                 if (entity) {
118149                   _map.zoomTo(entity);
118150                 }
118151               });
118152             }
118153
118154             _map.on('drawn.zoomToEntity', function () {
118155               if (!context.hasEntity(entityID)) { return; }
118156               _map.on('drawn.zoomToEntity', null);
118157               context.on('enter.zoomToEntity', null);
118158               context.enter(modeSelect(context, [entityID]));
118159             });
118160
118161             context.on('enter.zoomToEntity', function () {
118162               if (_mode.id !== 'browse') {
118163                 _map.on('drawn.zoomToEntity', null);
118164                 context.on('enter.zoomToEntity', null);
118165               }
118166             });
118167           };
118168
118169           var _minEditableZoom = 16;
118170           context.minEditableZoom = function(val) {
118171             if (!arguments.length) { return _minEditableZoom; }
118172             _minEditableZoom = val;
118173             if (_connection) {
118174               _connection.tileZoom(val);
118175             }
118176             return context;
118177           };
118178
118179           // String length limits in Unicode characters, not JavaScript UTF-16 code units
118180           context.maxCharsForTagKey = function () { return 255; };
118181           context.maxCharsForTagValue = function () { return 255; };
118182           context.maxCharsForRelationRole = function () { return 255; };
118183
118184           function cleanOsmString(val, maxChars) {
118185             // be lenient with input
118186             if (val === undefined || val === null) {
118187               val = '';
118188             } else {
118189               val = val.toString();
118190             }
118191
118192             // remove whitespace
118193             val = val.trim();
118194
118195             // use the canonical form of the string
118196             if (val.normalize) { val = val.normalize('NFC'); }
118197
118198             // trim to the number of allowed characters
118199             return utilUnicodeCharsTruncated(val, maxChars);
118200           }
118201           context.cleanTagKey = function (val) { return cleanOsmString(val, context.maxCharsForTagKey()); };
118202           context.cleanTagValue = function (val) { return cleanOsmString(val, context.maxCharsForTagValue()); };
118203           context.cleanRelationRole = function (val) { return cleanOsmString(val, context.maxCharsForRelationRole()); };
118204
118205
118206           /* History */
118207           var _inIntro = false;
118208           context.inIntro = function(val) {
118209             if (!arguments.length) { return _inIntro; }
118210             _inIntro = val;
118211             return context;
118212           };
118213
118214           // Immediately save the user's history to localstorage, if possible
118215           // This is called someteimes, but also on the `window.onbeforeunload` handler
118216           context.save = function () {
118217             // no history save, no message onbeforeunload
118218             if (_inIntro || context.container().select('.modal').size()) { return; }
118219
118220             var canSave;
118221             if (_mode && _mode.id === 'save') {
118222               canSave = false;
118223
118224               // Attempt to prevent user from creating duplicate changes - see #5200
118225               if (services.osm && services.osm.isChangesetInflight()) {
118226                 _history.clearSaved();
118227                 return;
118228               }
118229
118230             } else {
118231               canSave = context.selectedIDs().every(function (id) {
118232                 var entity = context.hasEntity(id);
118233                 return entity && !entity.isDegenerate();
118234               });
118235             }
118236
118237             if (canSave) {
118238               _history.save();
118239             }
118240             if (_history.hasChanges()) {
118241               return _t('save.unsaved_changes');
118242             }
118243           };
118244
118245           // Debounce save, since it's a synchronous localStorage write,
118246           // and history changes can happen frequently (e.g. when dragging).
118247           context.debouncedSave = debounce(context.save, 350);
118248
118249           function withDebouncedSave(fn) {
118250             return function() {
118251               var result = fn.apply(_history, arguments);
118252               context.debouncedSave();
118253               return result;
118254             };
118255           }
118256
118257
118258           /* Graph */
118259           context.hasEntity = function (id) { return _history.graph().hasEntity(id); };
118260           context.entity = function (id) { return _history.graph().entity(id); };
118261
118262
118263           /* Modes */
118264           var _mode;
118265           context.mode = function () { return _mode; };
118266           context.enter = function (newMode) {
118267             if (_mode) {
118268               _mode.exit();
118269               dispatch$1.call('exit', this$1, _mode);
118270             }
118271
118272             _mode = newMode;
118273             _mode.enter();
118274             dispatch$1.call('enter', this$1, _mode);
118275           };
118276
118277           context.selectedIDs = function () { return (_mode && _mode.selectedIDs && _mode.selectedIDs()) || []; };
118278           context.activeID = function () { return _mode && _mode.activeID && _mode.activeID(); };
118279
118280           var _selectedNoteID;
118281           context.selectedNoteID = function(noteID) {
118282             if (!arguments.length) { return _selectedNoteID; }
118283             _selectedNoteID = noteID;
118284             return context;
118285           };
118286
118287           // NOTE: Don't change the name of this until UI v3 is merged
118288           var _selectedErrorID;
118289           context.selectedErrorID = function(errorID) {
118290             if (!arguments.length) { return _selectedErrorID; }
118291             _selectedErrorID = errorID;
118292             return context;
118293           };
118294
118295
118296           /* Behaviors */
118297           context.install = function (behavior) { return context.surface().call(behavior); };
118298           context.uninstall = function (behavior) { return context.surface().call(behavior.off); };
118299
118300
118301           /* Copy/Paste */
118302           var _copyGraph;
118303           context.copyGraph = function () { return _copyGraph; };
118304
118305           var _copyIDs = [];
118306           context.copyIDs = function(val) {
118307             if (!arguments.length) { return _copyIDs; }
118308             _copyIDs = val;
118309             _copyGraph = _history.graph();
118310             return context;
118311           };
118312
118313           var _copyLonLat;
118314           context.copyLonLat = function(val) {
118315             if (!arguments.length) { return _copyLonLat; }
118316             _copyLonLat = val;
118317             return context;
118318           };
118319
118320
118321           /* Background */
118322           var _background;
118323           context.background = function () { return _background; };
118324
118325
118326           /* Features */
118327           var _features;
118328           context.features = function () { return _features; };
118329           context.hasHiddenConnections = function (id) {
118330             var graph = _history.graph();
118331             var entity = graph.entity(id);
118332             return _features.hasHiddenConnections(entity, graph);
118333           };
118334
118335
118336           /* Photos */
118337           var _photos;
118338           context.photos = function () { return _photos; };
118339
118340
118341           /* Map */
118342           var _map;
118343           context.map = function () { return _map; };
118344           context.layers = function () { return _map.layers(); };
118345           context.surface = function () { return _map.surface; };
118346           context.editableDataEnabled = function () { return _map.editableDataEnabled(); };
118347           context.surfaceRect = function () { return _map.surface.node().getBoundingClientRect(); };
118348           context.editable = function () {
118349             // don't allow editing during save
118350             var mode = context.mode();
118351             if (!mode || mode.id === 'save') { return false; }
118352             return _map.editableDataEnabled();
118353           };
118354
118355
118356           /* Debug */
118357           var _debugFlags = {
118358             tile: false,        // tile boundaries
118359             collision: false,   // label collision bounding boxes
118360             imagery: false,     // imagery bounding polygons
118361             target: false,      // touch targets
118362             downloaded: false   // downloaded data from osm
118363           };
118364           context.debugFlags = function () { return _debugFlags; };
118365           context.getDebug = function (flag) { return flag && _debugFlags[flag]; };
118366           context.setDebug = function(flag, val) {
118367             if (arguments.length === 1) { val = true; }
118368             _debugFlags[flag] = val;
118369             dispatch$1.call('change');
118370             return context;
118371           };
118372
118373
118374           /* Container */
118375           var _container = select(null);
118376           context.container = function(val) {
118377             if (!arguments.length) { return _container; }
118378             _container = val;
118379             _container.classed('ideditor', true);
118380             return context;
118381           };
118382           context.containerNode = function(val) {
118383             if (!arguments.length) { return context.container().node(); }
118384             context.container(select(val));
118385             return context;
118386           };
118387
118388           var _embed;
118389           context.embed = function(val) {
118390             if (!arguments.length) { return _embed; }
118391             _embed = val;
118392             return context;
118393           };
118394
118395
118396           /* Assets */
118397           var _assetPath = '';
118398           context.assetPath = function(val) {
118399             if (!arguments.length) { return _assetPath; }
118400             _assetPath = val;
118401             _mainFileFetcher.assetPath(val);
118402             return context;
118403           };
118404
118405           var _assetMap = {};
118406           context.assetMap = function(val) {
118407             if (!arguments.length) { return _assetMap; }
118408             _assetMap = val;
118409             _mainFileFetcher.assetMap(val);
118410             return context;
118411           };
118412
118413           context.asset = function (val) {
118414             if (/^http(s)?:\/\//i.test(val)) { return val; }
118415             var filename = _assetPath + val;
118416             return _assetMap[filename] || filename;
118417           };
118418
118419           context.imagePath = function (val) { return context.asset(("img/" + val)); };
118420
118421
118422           /* reset (aka flush) */
118423           context.reset = context.flush = function () {
118424             context.debouncedSave.cancel();
118425
118426             Array.from(_deferred).forEach(function (handle) {
118427               window.cancelIdleCallback(handle);
118428               _deferred.delete(handle);
118429             });
118430
118431             Object.values(services).forEach(function (service) {
118432               if (service && typeof service.reset === 'function') {
118433                 service.reset(context);
118434               }
118435             });
118436
118437             context.changeset = null;
118438
118439             _validator.reset();
118440             _features.reset();
118441             _history.reset();
118442             _uploader.reset();
118443
118444             // don't leave stale state in the inspector
118445             context.container().select('.inspector-wrap *').remove();
118446
118447             return context;
118448           };
118449
118450
118451           /* Projections */
118452           context.projection = geoRawMercator();
118453           context.curtainProjection = geoRawMercator();
118454
118455
118456           /* Init */
118457           context.init = function () {
118458
118459             instantiateInternal();
118460
118461             initializeDependents();
118462
118463             return context;
118464
118465             // Load variables and properties. No property of `context` should be accessed
118466             // until this is complete since load statuses are indeterminate. The order
118467             // of instantiation shouldn't matter.
118468             function instantiateInternal() {
118469
118470               _history = coreHistory(context);
118471               context.graph = _history.graph;
118472               context.pauseChangeDispatch = _history.pauseChangeDispatch;
118473               context.resumeChangeDispatch = _history.resumeChangeDispatch;
118474               context.perform = withDebouncedSave(_history.perform);
118475               context.replace = withDebouncedSave(_history.replace);
118476               context.pop = withDebouncedSave(_history.pop);
118477               context.overwrite = withDebouncedSave(_history.overwrite);
118478               context.undo = withDebouncedSave(_history.undo);
118479               context.redo = withDebouncedSave(_history.redo);
118480
118481               _validator = coreValidator(context);
118482               _uploader = coreUploader(context);
118483
118484               _background = rendererBackground(context);
118485               _features = rendererFeatures(context);
118486               _map = rendererMap(context);
118487               _photos = rendererPhotos(context);
118488
118489               _ui = uiInit(context);
118490             }
118491
118492             // Set up objects that might need to access properties of `context`. The order
118493             // might matter if dependents make calls to each other. Be wary of async calls.
118494             function initializeDependents() {
118495
118496               if (context.initialHashParams.presets) {
118497                 _mainPresetIndex.addablePresetIDs(new Set(context.initialHashParams.presets.split(',')));
118498               }
118499
118500               if (context.initialHashParams.locale) {
118501                 _mainLocalizer.preferredLocaleCodes(context.initialHashParams.locale);
118502               }
118503
118504               // kick off some async work
118505               _mainLocalizer.ensureLoaded();
118506               _background.ensureLoaded();
118507               _mainPresetIndex.ensureLoaded();
118508
118509               Object.values(services).forEach(function (service) {
118510                 if (service && typeof service.init === 'function') {
118511                   service.init();
118512                 }
118513               });
118514
118515               _map.init();
118516               _validator.init();
118517               _features.init();
118518               _photos.init();
118519
118520               if (services.maprules && context.initialHashParams.maprules) {
118521                 d3_json(context.initialHashParams.maprules)
118522                   .then(function (mapcss) {
118523                     services.maprules.init();
118524                     mapcss.forEach(function (mapcssSelector) { return services.maprules.addRule(mapcssSelector); });
118525                   })
118526                   .catch(function () { /* ignore */ });
118527               }
118528
118529               // if the container isn't available, e.g. when testing, don't load the UI
118530               if (!context.container().empty()) { _ui.ensureLoaded(); }
118531             }
118532           };
118533
118534
118535           return context;
118536         }
118537
118538         // When `debug = true`, we use `Object.freeze` on immutables in iD.
118539         // This is only done in testing because of the performance penalty.
118540         var debug = false;
118541         var d3 = {
118542           customEvent: customEvent,
118543           dispatch:  dispatch,
118544           event:  event,
118545           geoMercator: mercator,
118546           geoProjection: projection,
118547           polygonArea: d3_polygonArea,
118548           polygonCentroid: d3_polygonCentroid,
118549           select: select,
118550           selectAll: selectAll,
118551           timerFlush: timerFlush
118552         };
118553
118554         var iD = /*#__PURE__*/Object.freeze({
118555                 __proto__: null,
118556                 debug: debug,
118557                 d3: d3,
118558                 actionAddEntity: actionAddEntity,
118559                 actionAddMember: actionAddMember,
118560                 actionAddMidpoint: actionAddMidpoint,
118561                 actionAddVertex: actionAddVertex,
118562                 actionChangeMember: actionChangeMember,
118563                 actionChangePreset: actionChangePreset,
118564                 actionChangeTags: actionChangeTags,
118565                 actionCircularize: actionCircularize,
118566                 actionConnect: actionConnect,
118567                 actionCopyEntities: actionCopyEntities,
118568                 actionDeleteMember: actionDeleteMember,
118569                 actionDeleteMultiple: actionDeleteMultiple,
118570                 actionDeleteNode: actionDeleteNode,
118571                 actionDeleteRelation: actionDeleteRelation,
118572                 actionDeleteWay: actionDeleteWay,
118573                 actionDiscardTags: actionDiscardTags,
118574                 actionDisconnect: actionDisconnect,
118575                 actionExtract: actionExtract,
118576                 actionJoin: actionJoin,
118577                 actionMerge: actionMerge,
118578                 actionMergeNodes: actionMergeNodes,
118579                 actionMergePolygon: actionMergePolygon,
118580                 actionMergeRemoteChanges: actionMergeRemoteChanges,
118581                 actionMove: actionMove,
118582                 actionMoveMember: actionMoveMember,
118583                 actionMoveNode: actionMoveNode,
118584                 actionNoop: actionNoop,
118585                 actionOrthogonalize: actionOrthogonalize,
118586                 actionRestrictTurn: actionRestrictTurn,
118587                 actionReverse: actionReverse,
118588                 actionRevert: actionRevert,
118589                 actionRotate: actionRotate,
118590                 actionSplit: actionSplit,
118591                 actionStraightenNodes: actionStraightenNodes,
118592                 actionStraightenWay: actionStraightenWay,
118593                 actionUnrestrictTurn: actionUnrestrictTurn,
118594                 actionReflect: actionReflect,
118595                 actionUpgradeTags: actionUpgradeTags,
118596                 behaviorAddWay: behaviorAddWay,
118597                 behaviorBreathe: behaviorBreathe,
118598                 behaviorDrag: behaviorDrag,
118599                 behaviorDrawWay: behaviorDrawWay,
118600                 behaviorDraw: behaviorDraw,
118601                 behaviorEdit: behaviorEdit,
118602                 behaviorHash: behaviorHash,
118603                 behaviorHover: behaviorHover,
118604                 behaviorLasso: behaviorLasso,
118605                 behaviorOperation: behaviorOperation,
118606                 behaviorPaste: behaviorPaste,
118607                 behaviorSelect: behaviorSelect,
118608                 coreContext: coreContext,
118609                 coreFileFetcher: coreFileFetcher,
118610                 fileFetcher: _mainFileFetcher,
118611                 coreDifference: coreDifference,
118612                 coreGraph: coreGraph,
118613                 coreHistory: coreHistory,
118614                 coreLocalizer: coreLocalizer,
118615                 t: _t,
118616                 localizer: _mainLocalizer,
118617                 prefs: corePreferences,
118618                 coreTree: coreTree,
118619                 coreUploader: coreUploader,
118620                 coreValidator: coreValidator,
118621                 geoExtent: geoExtent,
118622                 geoLatToMeters: geoLatToMeters,
118623                 geoLonToMeters: geoLonToMeters,
118624                 geoMetersToLat: geoMetersToLat,
118625                 geoMetersToLon: geoMetersToLon,
118626                 geoMetersToOffset: geoMetersToOffset,
118627                 geoOffsetToMeters: geoOffsetToMeters,
118628                 geoScaleToZoom: geoScaleToZoom,
118629                 geoSphericalClosestNode: geoSphericalClosestNode,
118630                 geoSphericalDistance: geoSphericalDistance,
118631                 geoZoomToScale: geoZoomToScale,
118632                 geoAngle: geoAngle,
118633                 geoChooseEdge: geoChooseEdge,
118634                 geoEdgeEqual: geoEdgeEqual,
118635                 geoGetSmallestSurroundingRectangle: geoGetSmallestSurroundingRectangle,
118636                 geoHasLineIntersections: geoHasLineIntersections,
118637                 geoHasSelfIntersections: geoHasSelfIntersections,
118638                 geoRotate: geoRotate,
118639                 geoLineIntersection: geoLineIntersection,
118640                 geoPathHasIntersections: geoPathHasIntersections,
118641                 geoPathIntersections: geoPathIntersections,
118642                 geoPathLength: geoPathLength,
118643                 geoPointInPolygon: geoPointInPolygon,
118644                 geoPolygonContainsPolygon: geoPolygonContainsPolygon,
118645                 geoPolygonIntersectsPolygon: geoPolygonIntersectsPolygon,
118646                 geoViewportEdge: geoViewportEdge,
118647                 geoRawMercator: geoRawMercator,
118648                 geoVecAdd: geoVecAdd,
118649                 geoVecAngle: geoVecAngle,
118650                 geoVecCross: geoVecCross,
118651                 geoVecDot: geoVecDot,
118652                 geoVecEqual: geoVecEqual,
118653                 geoVecFloor: geoVecFloor,
118654                 geoVecInterp: geoVecInterp,
118655                 geoVecLength: geoVecLength,
118656                 geoVecLengthSquare: geoVecLengthSquare,
118657                 geoVecNormalize: geoVecNormalize,
118658                 geoVecNormalizedDot: geoVecNormalizedDot,
118659                 geoVecProject: geoVecProject,
118660                 geoVecSubtract: geoVecSubtract,
118661                 geoVecScale: geoVecScale,
118662                 geoOrthoNormalizedDotProduct: geoOrthoNormalizedDotProduct,
118663                 geoOrthoCalcScore: geoOrthoCalcScore,
118664                 geoOrthoMaxOffsetAngle: geoOrthoMaxOffsetAngle,
118665                 geoOrthoCanOrthogonalize: geoOrthoCanOrthogonalize,
118666                 modeAddArea: modeAddArea,
118667                 modeAddLine: modeAddLine,
118668                 modeAddPoint: modeAddPoint,
118669                 modeAddNote: modeAddNote,
118670                 modeBrowse: modeBrowse,
118671                 modeDragNode: modeDragNode,
118672                 modeDragNote: modeDragNote,
118673                 modeDrawArea: modeDrawArea,
118674                 modeDrawLine: modeDrawLine,
118675                 modeMove: modeMove,
118676                 modeRotate: modeRotate,
118677                 modeSave: modeSave,
118678                 modeSelect: modeSelect,
118679                 modeSelectData: modeSelectData,
118680                 modeSelectError: modeSelectError,
118681                 modeSelectNote: modeSelectNote,
118682                 operationCircularize: operationCircularize,
118683                 operationContinue: operationContinue,
118684                 operationCopy: operationCopy,
118685                 operationDelete: operationDelete,
118686                 operationDisconnect: operationDisconnect,
118687                 operationDowngrade: operationDowngrade,
118688                 operationExtract: operationExtract,
118689                 operationMerge: operationMerge,
118690                 operationMove: operationMove,
118691                 operationOrthogonalize: operationOrthogonalize,
118692                 operationPaste: operationPaste,
118693                 operationReflectShort: operationReflectShort,
118694                 operationReflectLong: operationReflectLong,
118695                 operationReverse: operationReverse,
118696                 operationRotate: operationRotate,
118697                 operationSplit: operationSplit,
118698                 operationStraighten: operationStraighten,
118699                 osmChangeset: osmChangeset,
118700                 osmEntity: osmEntity,
118701                 osmNode: osmNode,
118702                 osmNote: osmNote,
118703                 osmRelation: osmRelation,
118704                 osmWay: osmWay,
118705                 QAItem: QAItem,
118706                 osmIntersection: osmIntersection,
118707                 osmTurn: osmTurn,
118708                 osmInferRestriction: osmInferRestriction,
118709                 osmLanes: osmLanes,
118710                 osmOldMultipolygonOuterMemberOfRelation: osmOldMultipolygonOuterMemberOfRelation,
118711                 osmIsOldMultipolygonOuterMember: osmIsOldMultipolygonOuterMember,
118712                 osmOldMultipolygonOuterMember: osmOldMultipolygonOuterMember,
118713                 osmJoinWays: osmJoinWays,
118714                 get osmAreaKeys () { return osmAreaKeys; },
118715                 osmSetAreaKeys: osmSetAreaKeys,
118716                 osmTagSuggestingArea: osmTagSuggestingArea,
118717                 get osmPointTags () { return osmPointTags; },
118718                 osmSetPointTags: osmSetPointTags,
118719                 get osmVertexTags () { return osmVertexTags; },
118720                 osmSetVertexTags: osmSetVertexTags,
118721                 osmNodeGeometriesForTags: osmNodeGeometriesForTags,
118722                 osmOneWayTags: osmOneWayTags,
118723                 osmPavedTags: osmPavedTags,
118724                 osmIsInterestingTag: osmIsInterestingTag,
118725                 osmRoutableHighwayTagValues: osmRoutableHighwayTagValues,
118726                 osmFlowingWaterwayTagValues: osmFlowingWaterwayTagValues,
118727                 osmRailwayTrackTagValues: osmRailwayTrackTagValues,
118728                 presetCategory: presetCategory,
118729                 presetCollection: presetCollection,
118730                 presetField: presetField,
118731                 presetPreset: presetPreset,
118732                 presetManager: _mainPresetIndex,
118733                 presetIndex: presetIndex,
118734                 rendererBackgroundSource: rendererBackgroundSource,
118735                 rendererBackground: rendererBackground,
118736                 rendererFeatures: rendererFeatures,
118737                 rendererMap: rendererMap,
118738                 rendererPhotos: rendererPhotos,
118739                 rendererTileLayer: rendererTileLayer,
118740                 services: services,
118741                 serviceKeepRight: serviceKeepRight,
118742                 serviceImproveOSM: serviceImproveOSM,
118743                 serviceOsmose: serviceOsmose,
118744                 serviceMapillary: serviceMapillary,
118745                 serviceMapRules: serviceMapRules,
118746                 serviceNominatim: serviceNominatim,
118747                 serviceOpenstreetcam: serviceOpenstreetcam,
118748                 serviceOsm: serviceOsm,
118749                 serviceOsmWikibase: serviceOsmWikibase,
118750                 serviceStreetside: serviceStreetside,
118751                 serviceTaginfo: serviceTaginfo,
118752                 serviceVectorTile: serviceVectorTile,
118753                 serviceWikidata: serviceWikidata,
118754                 serviceWikipedia: serviceWikipedia,
118755                 svgAreas: svgAreas,
118756                 svgData: svgData,
118757                 svgDebug: svgDebug,
118758                 svgDefs: svgDefs,
118759                 svgKeepRight: svgKeepRight,
118760                 svgIcon: svgIcon,
118761                 svgGeolocate: svgGeolocate,
118762                 svgLabels: svgLabels,
118763                 svgLayers: svgLayers,
118764                 svgLines: svgLines,
118765                 svgMapillaryImages: svgMapillaryImages,
118766                 svgMapillarySigns: svgMapillarySigns,
118767                 svgMidpoints: svgMidpoints,
118768                 svgNotes: svgNotes,
118769                 svgMarkerSegments: svgMarkerSegments,
118770                 svgOpenstreetcamImages: svgOpenstreetcamImages,
118771                 svgOsm: svgOsm,
118772                 svgPassiveVertex: svgPassiveVertex,
118773                 svgPath: svgPath,
118774                 svgPointTransform: svgPointTransform,
118775                 svgPoints: svgPoints,
118776                 svgRelationMemberTags: svgRelationMemberTags,
118777                 svgSegmentWay: svgSegmentWay,
118778                 svgStreetside: svgStreetside,
118779                 svgTagClasses: svgTagClasses,
118780                 svgTagPattern: svgTagPattern,
118781                 svgTouch: svgTouch,
118782                 svgTurns: svgTurns,
118783                 svgVertices: svgVertices,
118784                 uiFieldDefaultCheck: uiFieldCheck,
118785                 uiFieldOnewayCheck: uiFieldCheck,
118786                 uiFieldCheck: uiFieldCheck,
118787                 uiFieldMultiCombo: uiFieldCombo,
118788                 uiFieldNetworkCombo: uiFieldCombo,
118789                 uiFieldSemiCombo: uiFieldCombo,
118790                 uiFieldTypeCombo: uiFieldCombo,
118791                 uiFieldCombo: uiFieldCombo,
118792                 uiFieldUrl: uiFieldText,
118793                 uiFieldIdentifier: uiFieldText,
118794                 uiFieldNumber: uiFieldText,
118795                 uiFieldTel: uiFieldText,
118796                 uiFieldEmail: uiFieldText,
118797                 uiFieldText: uiFieldText,
118798                 uiFieldAccess: uiFieldAccess,
118799                 uiFieldAddress: uiFieldAddress,
118800                 uiFieldCycleway: uiFieldCycleway,
118801                 uiFieldLanes: uiFieldLanes,
118802                 uiFieldLocalized: uiFieldLocalized,
118803                 uiFieldMaxspeed: uiFieldMaxspeed,
118804                 uiFieldStructureRadio: uiFieldRadio,
118805                 uiFieldRadio: uiFieldRadio,
118806                 uiFieldRestrictions: uiFieldRestrictions,
118807                 uiFieldTextarea: uiFieldTextarea,
118808                 uiFieldWikidata: uiFieldWikidata,
118809                 uiFieldWikipedia: uiFieldWikipedia,
118810                 uiFields: uiFields,
118811                 uiIntro: uiIntro,
118812                 uiPanelBackground: uiPanelBackground,
118813                 uiPanelHistory: uiPanelHistory,
118814                 uiPanelLocation: uiPanelLocation,
118815                 uiPanelMeasurement: uiPanelMeasurement,
118816                 uiInfoPanels: uiInfoPanels,
118817                 uiPaneBackground: uiPaneBackground,
118818                 uiPaneHelp: uiPaneHelp,
118819                 uiPaneIssues: uiPaneIssues,
118820                 uiPaneMapData: uiPaneMapData,
118821                 uiPanePreferences: uiPanePreferences,
118822                 uiSectionBackgroundDisplayOptions: uiSectionBackgroundDisplayOptions,
118823                 uiSectionBackgroundList: uiSectionBackgroundList,
118824                 uiSectionBackgroundOffset: uiSectionBackgroundOffset,
118825                 uiSectionChanges: uiSectionChanges,
118826                 uiSectionDataLayers: uiSectionDataLayers,
118827                 uiSectionEntityIssues: uiSectionEntityIssues,
118828                 uiSectionFeatureType: uiSectionFeatureType,
118829                 uiSectionMapFeatures: uiSectionMapFeatures,
118830                 uiSectionMapStyleOptions: uiSectionMapStyleOptions,
118831                 uiSectionOverlayList: uiSectionOverlayList,
118832                 uiSectionPhotoOverlays: uiSectionPhotoOverlays,
118833                 uiSectionPresetFields: uiSectionPresetFields,
118834                 uiSectionPrivacy: uiSectionPrivacy,
118835                 uiSectionRawMemberEditor: uiSectionRawMemberEditor,
118836                 uiSectionRawMembershipEditor: uiSectionRawMembershipEditor,
118837                 uiSectionRawTagEditor: uiSectionRawTagEditor,
118838                 uiSectionSelectionList: uiSectionSelectionList,
118839                 uiSectionValidationIssues: uiSectionValidationIssues,
118840                 uiSectionValidationOptions: uiSectionValidationOptions,
118841                 uiSectionValidationRules: uiSectionValidationRules,
118842                 uiSectionValidationStatus: uiSectionValidationStatus,
118843                 uiSettingsCustomBackground: uiSettingsCustomBackground,
118844                 uiSettingsCustomData: uiSettingsCustomData,
118845                 uiInit: uiInit,
118846                 uiAccount: uiAccount,
118847                 uiAttribution: uiAttribution,
118848                 uiChangesetEditor: uiChangesetEditor,
118849                 uiCmd: uiCmd,
118850                 uiCombobox: uiCombobox,
118851                 uiCommit: uiCommit,
118852                 uiCommitWarnings: uiCommitWarnings,
118853                 uiConfirm: uiConfirm,
118854                 uiConflicts: uiConflicts,
118855                 uiContributors: uiContributors,
118856                 uiCurtain: uiCurtain,
118857                 uiDataEditor: uiDataEditor,
118858                 uiDataHeader: uiDataHeader,
118859                 uiDisclosure: uiDisclosure,
118860                 uiEditMenu: uiEditMenu,
118861                 uiEntityEditor: uiEntityEditor,
118862                 uiFeatureInfo: uiFeatureInfo,
118863                 uiFeatureList: uiFeatureList,
118864                 uiField: uiField,
118865                 uiFieldHelp: uiFieldHelp,
118866                 uiFlash: uiFlash,
118867                 uiFormFields: uiFormFields,
118868                 uiFullScreen: uiFullScreen,
118869                 uiGeolocate: uiGeolocate,
118870                 uiImproveOsmComments: uiImproveOsmComments,
118871                 uiImproveOsmDetails: uiImproveOsmDetails,
118872                 uiImproveOsmEditor: uiImproveOsmEditor,
118873                 uiImproveOsmHeader: uiImproveOsmHeader,
118874                 uiInfo: uiInfo,
118875                 uiInspector: uiInspector,
118876                 uiIssuesInfo: uiIssuesInfo,
118877                 uiKeepRightDetails: uiKeepRightDetails,
118878                 uiKeepRightEditor: uiKeepRightEditor,
118879                 uiKeepRightHeader: uiKeepRightHeader,
118880                 uiLasso: uiLasso,
118881                 uiLoading: uiLoading,
118882                 uiMapInMap: uiMapInMap,
118883                 uiModal: uiModal,
118884                 uiNotice: uiNotice,
118885                 uiNoteComments: uiNoteComments,
118886                 uiNoteEditor: uiNoteEditor,
118887                 uiNoteHeader: uiNoteHeader,
118888                 uiNoteReport: uiNoteReport,
118889                 uiPopover: uiPopover,
118890                 uiPresetIcon: uiPresetIcon,
118891                 uiPresetList: uiPresetList,
118892                 uiRestore: uiRestore,
118893                 uiScale: uiScale,
118894                 uiSidebar: uiSidebar,
118895                 uiSourceSwitch: uiSourceSwitch,
118896                 uiSpinner: uiSpinner,
118897                 uiSplash: uiSplash,
118898                 uiStatus: uiStatus,
118899                 uiSuccess: uiSuccess,
118900                 uiTagReference: uiTagReference,
118901                 uiToggle: uiToggle,
118902                 uiTooltip: uiTooltip,
118903                 uiVersion: uiVersion,
118904                 uiViewOnOSM: uiViewOnOSM,
118905                 uiViewOnKeepRight: uiViewOnKeepRight,
118906                 uiZoom: uiZoom,
118907                 utilAesEncrypt: utilAesEncrypt,
118908                 utilAesDecrypt: utilAesDecrypt,
118909                 utilArrayChunk: utilArrayChunk,
118910                 utilArrayDifference: utilArrayDifference,
118911                 utilArrayFlatten: utilArrayFlatten,
118912                 utilArrayGroupBy: utilArrayGroupBy,
118913                 utilArrayIdentical: utilArrayIdentical,
118914                 utilArrayIntersection: utilArrayIntersection,
118915                 utilArrayUnion: utilArrayUnion,
118916                 utilArrayUniq: utilArrayUniq,
118917                 utilArrayUniqBy: utilArrayUniqBy,
118918                 utilAsyncMap: utilAsyncMap,
118919                 utilCleanTags: utilCleanTags,
118920                 utilCombinedTags: utilCombinedTags,
118921                 utilDeepMemberSelector: utilDeepMemberSelector,
118922                 utilDetect: utilDetect,
118923                 utilDisplayName: utilDisplayName,
118924                 utilDisplayNameForPath: utilDisplayNameForPath,
118925                 utilDisplayType: utilDisplayType,
118926                 utilDisplayLabel: utilDisplayLabel,
118927                 utilEntityRoot: utilEntityRoot,
118928                 utilEditDistance: utilEditDistance,
118929                 utilEntitySelector: utilEntitySelector,
118930                 utilEntityOrMemberSelector: utilEntityOrMemberSelector,
118931                 utilEntityOrDeepMemberSelector: utilEntityOrDeepMemberSelector,
118932                 utilFastMouse: utilFastMouse,
118933                 utilFunctor: utilFunctor,
118934                 utilGetAllNodes: utilGetAllNodes,
118935                 utilGetSetValue: utilGetSetValue,
118936                 utilHashcode: utilHashcode,
118937                 utilHighlightEntities: utilHighlightEntities,
118938                 utilKeybinding: utilKeybinding,
118939                 utilNoAuto: utilNoAuto,
118940                 utilObjectOmit: utilObjectOmit,
118941                 utilPrefixCSSProperty: utilPrefixCSSProperty,
118942                 utilPrefixDOMProperty: utilPrefixDOMProperty,
118943                 utilQsString: utilQsString,
118944                 utilRebind: utilRebind,
118945                 utilSafeClassName: utilSafeClassName,
118946                 utilSetTransform: utilSetTransform,
118947                 utilSessionMutex: utilSessionMutex,
118948                 utilStringQs: utilStringQs,
118949                 utilTagDiff: utilTagDiff,
118950                 utilTagText: utilTagText,
118951                 utilTiler: utilTiler,
118952                 utilTotalExtent: utilTotalExtent,
118953                 utilTriggerEvent: utilTriggerEvent,
118954                 utilUnicodeCharsCount: utilUnicodeCharsCount,
118955                 utilUnicodeCharsTruncated: utilUnicodeCharsTruncated,
118956                 utilUniqueDomId: utilUniqueDomId,
118957                 utilWrap: utilWrap,
118958                 validationAlmostJunction: validationAlmostJunction,
118959                 validationCloseNodes: validationCloseNodes,
118960                 validationCrossingWays: validationCrossingWays,
118961                 validationDisconnectedWay: validationDisconnectedWay,
118962                 validationFormatting: validationFormatting,
118963                 validationHelpRequest: validationHelpRequest,
118964                 validationImpossibleOneway: validationImpossibleOneway,
118965                 validationIncompatibleSource: validationIncompatibleSource,
118966                 validationMaprules: validationMaprules,
118967                 validationMismatchedGeometry: validationMismatchedGeometry,
118968                 validationMissingRole: validationMissingRole,
118969                 validationMissingTag: validationMissingTag,
118970                 validationOutdatedTags: validationOutdatedTags,
118971                 validationPrivateData: validationPrivateData,
118972                 validationSuspiciousName: validationSuspiciousName,
118973                 validationUnsquareWay: validationUnsquareWay
118974         });
118975
118976         // polyfill requestIdleCallback
118977         window.requestIdleCallback = window.requestIdleCallback ||
118978             function(cb) {
118979                 var start = Date.now();
118980                 return window.requestAnimationFrame(function() {
118981                     cb({
118982                         didTimeout: false,
118983                         timeRemaining: function() {
118984                             return Math.max(0, 50 - (Date.now() - start));
118985                         }
118986                     });
118987                 });
118988             };
118989
118990         window.cancelIdleCallback = window.cancelIdleCallback ||
118991             function(id) {
118992                 window.cancelAnimationFrame(id);
118993             };
118994         window.iD = iD;
118995
118996 }());